using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Xml;
using CommandLine;
using CommandLine.Text;
using Isis;
using Mono.Unix;
using Mono.Unix.Native;
using State = System.UInt32;

/**
 * Arguments needed for getting the information of a node.
 */
delegate void askInfoArgs ();
/**
 * Arguments needed for a leader registration.
 */
delegate void registerLeaderArgs (string nodename);
/**
 * Arguments needed to run a process.
 */
delegate void runProcessArgs (string procName, string path, string exec, string args);
/**
 * Arguments needed to kill a process.
 */
delegate void killProcessArgs (string procName);
/**
 * Arguments needed for updating the failure information in CloudMake leader when an application
 * process in a local node fails.
 */
delegate void stoppedProcArgs (string ipAddress, List<string> procIds);
/**
 * Arguments needed for updating the status file of the CloudMake leader from a local node.
 */
delegate void updateStateArgs (string nodename, List<string> procs, List<string> msgs);
/**
 * Arguments needed for updating the configuration file of a local node from CloudMake leader.
 */
delegate void updateConfigArgs (List<string> path, string name, string content);
/**
 * Arguments needed for updating the configuration file of a local node from CloudMake leader.
 */
delegate void commitConfigArgs (int rank, long logicalClock);
/**
 * Arguments needed for killing a process of a local node from CloudMake leader.
 */
delegate void killArgs (string procId);
/**
 * Class for defining all the options that can be passed as parameters in the command line.
 */
public class Options
{
	[Option('d', "debug-enabled", DefaultValue = false, HelpText = "Debug Mode on or off.")]
	public bool debug { get; set; }

	[Option('n', "name", Required = true, HelpText = "Hostname of the node.")]
	public string name { get; set; }

	[Option('g', "groupname", DefaultValue = "CloudMake",
	        HelpText = "Name of the group formed. Useful if there are two or more CloudMake Groups.")]
	public string groupname { get; set; }

	[Option('l', "leader", DefaultValue = false, HelpText = "Whether or not this instance is a CloudMake Leader.")]
	public bool leader { get; set; }

	[Option('m', "master", DefaultValue = null, HelpText = "The Isis Master IP address.")]
	public string master { get; set; }

	[Option('w', "nworkers", DefaultValue = 0, HelpText = "The number defines the number of workers in the group. " +
		"Only applicable to instances which act as Isis masters.")]
	public int nworkers { get; set; }

	[Option('p', "parallel-enabled", DefaultValue = false,
	        HelpText = "Allow parallel execution of management programs.")]
	public bool parallel { get; set; }

	[Option('b', "batching-enabled", DefaultValue = false,
	        HelpText = "Enable batching of monitoring and configuration messages")]
	public bool batching { get; set; }

	[Option('t', "timeout", DefaultValue = 200, HelpText = "Time in milliseconds to trigger monitoring of processes.")]
	public int timeout { get; set; }

	[ParserState]
	public IParserState lastParserState { get; set; }

	[HelpOption]
	public string GetUsage ()
	{
		HelpText help = new HelpText ();
		help.Copyright = new CopyrightInfo ("Theodoros Gkountouvas", 2015);
		help.AddPreOptionsLine ("Contact me at my email: tedgoud@cs.cornell.edu");
		help.AddPreOptionsLine ("CloudMake is built on top of the Isis2 library and provides a nice abstraction to the " +
			"user who overcomes all the difficulties that have to do with monitoring and configuration of the " +
			"system. The application designer needs to handle the following tasks:"
		);
		help.AddPreOptionsLine ("1. Launch CloudMake processes in every computer node."
		);
		help.AddPreOptionsLine ("2. Provide information to CloudMake process for every Application Process the user " +
			"needs to run. More specifically an associated id (needs to be unique for the corresponding Application " +
			"Process in the computer node), the executable command, status and config file names (they are going to " +
			"be used for monitoring and configuration), when a new computer node joins the CloudMake Group."
		);
		help.AddPreOptionsLine ("3. Provide the necessary monitoring information in the status file specified in 2."
		);
		help.AddPreOptionsLine ("4. Modify the configuration of every Application Process according to the " +
			"config file specified in 2 when necessary."
		);
		help.AddPreOptionsLine ("5. Provide management policies by writing a makefile that take as an input all the " +
			"status files along with information about new and failed computer nodes and failed application " +
			"processes in the system and produces the appropriate configuration files."
		);
		help.AddOptions (this);
		return help;
	}
}

public class FA
{
	public enum Response
	{
		ACCEPT,
		NOT_REJECT,
		REJECT,
	};

	protected HashSet<char> _S;
	protected HashSet<State> _Q;

	public HashSet<Char> GetAlphabet ()
	{
		return _S;
	}

	public HashSet<State> GetStates ()
	{
		return _Q;
	}

	public uint GetNrStates ()
	{
		return Convert.ToUInt32 (_Q.Count);
	}
}

public class DFA : FA
{
	private Dictionary <State, Dictionary<Char, State>> _d;
	private HashSet<State> _finalStates;

	public DFA (Dictionary <State, Dictionary<Char, State>> d, HashSet<State> finalStates)
	{
		_S = new HashSet<char> ();
		_Q = new HashSet<State> ();

		foreach (State startState in d.Keys) {
			_Q.Add (startState);
			foreach (char c in d[startState].Keys) {
				_S.Add (c);
				_Q.Add (d [startState] [c]);
			}
		}
		_d = d;
		_finalStates = finalStates;
		foreach (State finalState in finalStates)
			if (!_Q.Contains (finalState))
				_Q.Add (finalState);
	}

	public Dictionary<State,Dictionary<char,State>> GetTransitionFunction ()
	{
		return _d;
	}

	public HashSet<State> GetFinalStates ()
	{
		return _finalStates;
	}

	override public string ToString ()
	{
		string res = "";
		
		res += "S:";
		foreach (char c in _S)
			res += " " + c.ToString ();
		res += Environment.NewLine + "Q:";
		foreach (State state in _Q)
			res += " " + state.ToString ();
		res += Environment.NewLine + "d:";
		foreach (State startState in _d.Keys)
			foreach (char c in _d[startState].Keys)
				res += Environment.NewLine + "(" + startState.ToString () + "," + c.ToString () + ") -> " +
					_d [startState] [c].ToString ();
		res += Environment.NewLine + "Final States :";
		foreach (State finalState in _finalStates)
			res += Environment.NewLine + finalState.ToString ();
		res += Environment.NewLine;
		return res;
	}

	static public DFA operator * (DFA dfa1, DFA dfa2)
	{
		if ((dfa1 == null) || (dfa2 == null))
			return null;

		HashSet<char> S = new HashSet<char> (dfa1.GetAlphabet ());
		S.IntersectWith (dfa2.GetAlphabet ());
		Dictionary <State, Dictionary<Char, State>> d = new Dictionary<State, Dictionary<char, State>> ();
		Dictionary <State, Dictionary<Char, State>> d1 = dfa1.GetTransitionFunction ();
		Dictionary <State, Dictionary<Char, State>> d2 = dfa2.GetTransitionFunction ();
		HashSet<State> finalStates = new HashSet<State> ();
		HashSet<State> finalStates1 = dfa1.GetFinalStates ();
		HashSet<State> finalStates2 = dfa2.GetFinalStates ();
		List<Tuple<State,State>> states = new List<Tuple<State,State>> ();
		List<Tuple<State,State>> unexploredStates = new List<Tuple<State,State>> ();
		Dictionary<Tuple<State,State>,State> transform = new Dictionary<Tuple<State,State>, State> ();
		Tuple<State,State> tempInitialState = new Tuple<State,State> (0, 0);
		State curState = 1;

		states.Add (tempInitialState);
		unexploredStates.Add (tempInitialState);
		transform.Add (tempInitialState, 0);
		while (unexploredStates.Count != 0) {
			Tuple<State,State> tempStartState = unexploredStates [0];
			State startState1 = tempStartState.Item1;
			State startState2 = tempStartState.Item2;

			if ((finalStates1.Contains (tempStartState.Item1)) &&
				(finalStates2.Contains (tempStartState.Item2)))
				finalStates.Add (transform [tempStartState]);
			if (d1.ContainsKey (startState1) && d2.ContainsKey (startState2)) {
				Dictionary<char, State> nestedD = new Dictionary<char, State> ();
				Dictionary<char, State> nestedD1 = d1 [startState1];
				Dictionary<char, State> nestedD2 = d2 [startState2];

				foreach (Char c in S) {
					if ((nestedD1.ContainsKey (c)) && (nestedD2.ContainsKey (c))) {
						Tuple<State,State> tempEndState = new Tuple<State,State> (nestedD1 [c], nestedD2 [c]);

						if (!transform.ContainsKey (tempEndState)) {
							states.Add (tempEndState);
							unexploredStates.Add (tempEndState);
							transform.Add (tempEndState, curState);
							curState++;
						}
						nestedD.Add (c, transform [tempEndState]);
					}
				}
				if (!transform.ContainsKey (tempStartState))
					throw new Exception ("Transform should contain this state.");
				if (nestedD.Count > 0)
					d.Add (transform [tempStartState], nestedD);
			}
			unexploredStates.RemoveAt (0);
		}

		if (d.Count == 0)
			return null;

		return new DFA (d, finalStates);
	}

	static public DFA operator - (DFA dfa1, DFA dfa2)
	{
		if (dfa1 == null)
			return null;

		if (dfa2 == null)
			return dfa1;

		HashSet<char> S = new HashSet<char> (dfa1.GetAlphabet ());
		Dictionary <State, Dictionary<Char, State>> d = new Dictionary<State, Dictionary<char, State>> ();
		Dictionary <State, Dictionary<Char, State>> d1 = dfa1.GetTransitionFunction ();
		Dictionary <State, Dictionary<Char, State>> d2 = dfa2.GetTransitionFunction ();
		HashSet<State> finalStates = new HashSet<State> ();
		HashSet<State> finalStates1 = dfa1.GetFinalStates ();
		HashSet<State> finalStates2 = dfa2.GetFinalStates ();
		List<Tuple<State,State>> states = new List<Tuple<State,State>> ();
		List<Tuple<State,State>> unexploredStates = new List<Tuple<State,State>> ();
		Dictionary<Tuple<State,State>,State> transform = new Dictionary<Tuple<State,State>, State> ();
		Tuple<State,State> tempInitialState;
		State curState = 1;

		tempInitialState = new Tuple<State,State> (0, 0);
		states = new List<Tuple<State,State>> ();
		states.Add (tempInitialState);
		unexploredStates = new List<Tuple<State,State>> ();
		unexploredStates.Add (tempInitialState);
		transform.Add (tempInitialState, 0);
		while (unexploredStates.Count != 0) {
			Tuple<State,State> tempStartState = unexploredStates [0];
			State startState1 = tempStartState.Item1;
			State startState2 = tempStartState.Item2;

			if ((finalStates1.Contains (tempStartState.Item1)) &&
				(!finalStates2.Contains (tempStartState.Item2)))
				finalStates.Add (transform [tempStartState]);
			if (d1.ContainsKey (startState1)) {
				Dictionary<char, State> nestedD = new Dictionary<char, State> ();
				Dictionary<char, State> nestedD1 = d1 [startState1];
				Dictionary<char, State> nestedD2 = (d2.ContainsKey (startState2)) ? d2 [startState2] :
					new Dictionary<char, State> ();

				foreach (Char c in S) {
					if ((nestedD1.ContainsKey (c)) && (nestedD2.ContainsKey (c))) {
						Tuple<State,State> tempEndState = new Tuple<State,State> (nestedD1 [c], nestedD2 [c]);

						if (!transform.ContainsKey (tempEndState)) {
							states.Add (tempEndState);
							unexploredStates.Add (tempEndState);
							transform.Add (tempEndState, curState);
							curState++;
						}
						nestedD.Add (c, transform [tempEndState]);
					} else if (nestedD1.ContainsKey (c)) {
						Tuple<State,State> tempEndState = new Tuple<State,State> (nestedD1 [c], UInt32.MaxValue);

						if (!transform.ContainsKey (tempEndState)) {
							states.Add (tempEndState);
							unexploredStates.Add (tempEndState);
							transform.Add (tempEndState, curState);
							curState++;
						}
						nestedD.Add (c, transform [tempEndState]);
					}
				}
				if (!transform.ContainsKey (tempStartState))
					throw new Exception ("Transform should contain this state.");
				if (nestedD.Count > 0)
					d.Add (transform [tempStartState], nestedD);
			}
			unexploredStates.RemoveAt (0);
		}

		if (d.Count == 0)
			return null;

		return new DFA (d, finalStates);
	}

	public DFA Minimize ()
	{
		List<HashSet<State>> P = new List<HashSet<State>> ();
		List<HashSet<State>> W = new List<HashSet<State>> ();
		Dictionary<State, HashSet<State>> transformA = new Dictionary<State, HashSet<State>> ();
		Dictionary<HashSet<State>, State> transformB = new Dictionary<HashSet<State>, State> ();
		Dictionary <State, Dictionary<Char, State>> d = new Dictionary<State, Dictionary<char, State>> ();
		HashSet<State> finalStates = new HashSet<State> ();
		State curState = 1;

		P.Add (_finalStates);
		P.Add (new HashSet<State> (_Q));
		P [1].ExceptWith (_finalStates);
		W.Add (_finalStates);

		while (W.Count > 0) {
			foreach (char c in _S) {
				HashSet<State> X = new HashSet<State> ();

				foreach (State state in _d.Keys)
					if ((_d [state].ContainsKey (c)) && (W [0].Contains (_d [state] [c])))
						X.Add (state);

				if (X.Count == 0)
					continue;

				List<HashSet<State>> tempP = new List<HashSet<State>> (P);

				foreach (HashSet<State> Y in P) {
					HashSet<State> Y1 = new HashSet<State> (Y);
					HashSet<State> Y2 = new HashSet<State> (Y);

					Y1.IntersectWith (X);
					Y2.ExceptWith (X);
					if ((Y1.Count > 0) && (Y2.Count > 0)) {
						tempP.Remove (Y);
						tempP.Add (Y1);
						tempP.Add (Y2);
						if (W.Contains (Y)) {
							W.Remove (Y);
							W.Add (Y1);
							W.Add (Y2);
						} else {
							if (Y1.Count <= Y2.Count)
								W.Add (Y1);
							else
								W.Add (Y2);
						}
					}
				}
				P = tempP;
			}
			W.RemoveAt (0);
		}

		/*
		Console.Out.WriteLine ("P:");
		foreach (HashSet<State> states in P) {
			Console.Out.Write ("{");
			foreach (State state in states)
				Console.Out.Write (" " + state.ToString ());
			Console.Out.WriteLine (" }");
		}
		Console.Out.WriteLine ();
		*/

		foreach (State state in _Q) {
			foreach (HashSet<State> tempSet in P) {
				if (tempSet.Contains (state))
					transformA.Add (state, tempSet);
			}
			if (!transformA.ContainsKey (state))
				throw new Exception ("Something is wrong with Hopcroft algorithm.");
		}

		foreach (HashSet<State> tempSet in P) {
			if (tempSet.Contains (0))
				transformB.Add (tempSet, 0);
			else
				transformB.Add (tempSet, curState++);
		}

		/*
		Console.Out.WriteLine ("Transform:");
		foreach (State state in _Q)
			Console.Out.WriteLine(state.ToString() + " -> " + transformB[transformA[state]]);
		Console.Out.WriteLine ();
		*/

		foreach (State startState in _d.Keys) {
			Dictionary<Char, State> nestedD;
			State tempStartState = transformB [transformA [startState]];

			if (d.ContainsKey (tempStartState))
				nestedD = d [tempStartState];
			else
				nestedD = new Dictionary<char, State> ();
			foreach (Char c in _d[startState].Keys) {
				if (nestedD.ContainsKey (c)) {
					if (nestedD [c] != transformB [transformA [_d [startState] [c]]])
						throw new Exception ("Minimize: d[" + startState.ToString () + "][" + c.ToString () + "] -> " +
							_d [startState] [c] + " instead of going to " + nestedD [c]);
				} else {
					nestedD.Add (c, transformB [transformA [_d [startState] [c]]]);
				}
			}
			d [tempStartState] = nestedD;
		}

		foreach (State finalState in _finalStates)
			finalStates.Add (transformB [transformA [finalState]]);

		return new DFA (d, finalStates);
	}

	public DFA RemoveRedudantStates ()
	{
		Dictionary <State, Dictionary<Char, State>> d = new Dictionary<State, Dictionary<char, State>> ();
		HashSet<State> finalStates = new HashSet<State> ();
		HashSet<State> redudantStates = new HashSet<State> ();
		Dictionary<State,State> transform = new Dictionary<State, State> ();
		State curState = 1;

		foreach (State state in _Q) {
			if (!ReachesFinalStates (state)) {
				redudantStates.Add (state);
			} else {
				if (state == 0)
					transform.Add (0, 0);
				else
					transform.Add (state, curState++);
			}
		}

		foreach (State startState in _d.Keys) {
			if (redudantStates.Contains (startState))
				continue;

			Dictionary<char,State> nestedD = new Dictionary<char, State> ();

			foreach (char c in _d[startState].Keys) {
				if (redudantStates.Contains (_d [startState] [c]))
					continue;
				nestedD.Add (c, transform [_d [startState] [c]]);
			}
			if (nestedD.Count > 0)
				d.Add (transform [startState], nestedD);
		}

		foreach (State finalState in _finalStates)
			finalStates.Add (transform [finalState]);

		if (d.Count == 0)
			return null;

		return new DFA (d, finalStates);
	}

	private bool ReachesFinalStates (State state)
	{
		if (!_Q.Contains (state))
			throw new Exception ("State does not exist in the DFA.");

		if (_finalStates.Contains (state))
			return true;

		List<State> reachableStates = new List<State> ();
		List<State> unexploredStates = new List<State> ();
		State curState, tempState;

		reachableStates.Add (state);
		unexploredStates.Add (state);
		while (unexploredStates.Count > 0) {
			curState = unexploredStates [0];
			if (_d.ContainsKey (curState)) {
				foreach (char c in _d[curState].Keys) {
					tempState = _d [curState] [c];
					if (reachableStates.Contains (tempState))
						continue;
					if (_finalStates.Contains (tempState))
						return true;
					reachableStates.Add (tempState);
					unexploredStates.Add (tempState);
				}
			}
			unexploredStates.RemoveAt (0);
		}

		return false;
	}

	public bool EqualState (List<State> states1, List<State> states2)
	{
		if (states1.Count != states2.Count)
			return false;

		states1.Sort ();
		states2.Sort ();
		for (int i = 0; i < states1.Count; i++)
			if (states1 [i] != states2 [i])
				return false;
		return true;
	}

	static public bool ContainsState (List<HashSet<State>> states, HashSet<State> state)
	{
		foreach (HashSet<State> tempState in states)
			if (state.SetEquals (tempState))
				return true;
		return false;
	}

	public Response Parse (string filename)
	{
		char[] filenameChars = filename.ToCharArray ();
		State curState = 0;

		foreach (char c in filenameChars) {
			if ((_d.ContainsKey (curState)) && (_d [curState].ContainsKey (c)))
				curState = _d [curState] [c];
			else
				return Response.REJECT;
		}

		if (_finalStates.Contains (curState))
			return Response.ACCEPT;
		else
			return Response.NOT_REJECT;
	}
}

public class NFA : FA
{
	private Dictionary<State,Dictionary<char,HashSet<State>>> _d;
	private State _finalState;

	public NFA (Dictionary<State,Dictionary<char,HashSet<State>>> d, State finalState)
	{
		_S = new HashSet<char> ();
		_Q = new HashSet<State> ();

		foreach (State startState in d.Keys) {
			_Q.Add (startState);
			foreach (char c in d[startState].Keys) {
				_S.Add (c);
				foreach (State endState in d[startState][c])
					_Q.Add (endState);
			}
		}
		_d = d;
		_finalState = finalState;
		if (!_Q.Contains (finalState))
			_Q.Add (finalState);
	}

	public NFA (NFA nfa)
	{
		Dictionary<State,Dictionary<char,HashSet<State>>> d = nfa.GetTransitionFunction ();
		State finalState = nfa.GetFinalState ();

		_S = new HashSet<char> ();
		_Q = new HashSet<State> ();
		_d = new Dictionary<State, Dictionary<char, HashSet<State>>> ();

		foreach (State startState in d.Keys) {
			Dictionary<char, HashSet<State>> nestedD = new Dictionary<char, HashSet<State>> ();

			_Q.Add (startState);
			foreach (char c in d[startState].Keys) {
				HashSet<State> nestedSet = new HashSet<State> ();

				_S.Add (c);
				foreach (State endState in d[startState][c]) {
					_Q.Add (endState);
					nestedSet.Add (endState);
				}
				nestedD.Add (c, nestedSet);
			}
			_d.Add (startState, nestedD);
		}
		_finalState = finalState;
		if (!_Q.Contains (finalState))
			_Q.Add (finalState);
	}

	public Dictionary<State,Dictionary<char,HashSet<State>>> GetTransitionFunction ()
	{
		return _d;
	}

	public State GetFinalState ()
	{
		return _finalState;
	}

	override public string ToString ()
	{
		string res = "";

		res += "Q:";
		foreach (State s in _Q)
			res += " " + s.ToString ();
		res += Environment.NewLine + "S:";
		foreach (char c in _S)
			res += " " + c.ToString ();
		res += Environment.NewLine + "d:";
		foreach (State startState in _d.Keys)
			foreach (char c in _d[startState].Keys)
				foreach (State endState in _d[startState][c])
					res += Environment.NewLine + "(" + startState + "," + c + ") -> " + endState;
		res += Environment.NewLine + "Final State: " + _finalState.ToString () + Environment.NewLine;
		return res;
	}

	static public Tuple<NFA,List<Node>> makeNFAFromParseTreeNode (Node node)
	{
		Tuple<NFA,List<Node>> res = new Tuple<NFA, List<Node>> (null, null);
		Tuple<NFA,List<Node>> temp;
		NFA nfa, tempNfa;
		List<Node> nodeList, tempNodeList;
		Dictionary<State,Dictionary<char,HashSet<State>>> tempD, d;
		State tempFinalState, finalState, curState;
		List<Node> children;
		Dictionary<char,HashSet<State>> nestedD;
		HashSet<State> nestedSet;
		int count;
		uint nrStates;

		switch (node.GetNodeType ()) {
		case Node.Type.TENTRY:
			children = node.GetChildren ();

			if (((children.Count != 2) || (children [0].GetNodeType () != Node.Type.TFILEPATH) ||
				(children [1].GetNodeType () != Node.Type.TXMLPATH)) &&
				((children.Count != 1) || (children [0].GetNodeType () != Node.Type.TFILEPATH)))
				throw new ParserException ("Node of type TENTRY should have TFILEPATH and TXMLPATH children or a " +
					"TFILEPATH only.");

			res = makeNFAFromParseTreeNode (children [0]);

			if (children.Count == 1)
				return res;

			nfa = res.Item1;
			nodeList = res.Item2;
			if (nodeList != null) {
				nodeList.Add (new Node ("SEQUENCE(CHAR(.),CHAR(x),CHAR(m),CHAR(l))"));
				nodeList.Add (children [1]);
				res = new Tuple<NFA, List<Node>> (nfa, nodeList);
				break;
			}

			d = new Dictionary<State,Dictionary<char,HashSet<State>>> ();
			nestedD = new Dictionary<char, HashSet<State>> ();
			nestedD.Add ('.', new HashSet<State> (new State[] { 1 }));
			d.Add (0, nestedD);
			nestedD = new Dictionary<char, HashSet<State>> ();
			nestedD.Add ('x', new HashSet<State> (new State[] { 2 }));
			d.Add (1, nestedD);
			nestedD = new Dictionary<char, HashSet<State>> ();
			nestedD.Add ('m', new HashSet<State> (new State[] { 3 }));
			d.Add (2, nestedD);
			nestedD = new Dictionary<char, HashSet<State>> ();
			nestedD.Add ('l', new HashSet<State> (new State[] { 4 }));
			d.Add (3, nestedD);
			tempNfa = new NFA (d, 4);
			nfa += tempNfa;

			temp = makeNFAFromParseTreeNode (children [1]);
			tempNfa = temp.Item1;
			tempNodeList = temp.Item2;
			nfa += tempNfa;
			nodeList = tempNodeList;
			break;
		case Node.Type.TFILEPATH:
			children = node.GetChildren ();
			count = 0;
			nfa = null;
			tempNodeList = null;

			while ((count != children.Count) && (tempNodeList == null)) {
				temp = makeNFAFromParseTreeNode (children [count]);
				tempNfa = temp.Item1;
				tempNodeList = temp.Item2;
				nfa += tempNfa;
				count++;
			}
			nodeList = tempNodeList;
			while (count != children.Count) {
				nodeList.Add (children [count]);
				count++;
			}
			break;
		case Node.Type.TXMLPATH:
			Node tempNode = new Node ("SEQUENCE(ASTERISK(ATOM(SEQUENCE(LEFT_BRACKET(),SEQUENCE(ASTERISK(ATOM(" +
				"SEQUENCE(CHOICE(RANGE(CHAR(A),CHAR(Z)),RANGE(CHAR(a),CHAR(z)),RANGE(CHAR(0),CHAR(9)),CHAR(@),CHAR(_)" +
				"))))),RIGHT_BRACKET()))))");

			children = node.GetChildren ();
			count = 0;
			nfa = null;
			tempNodeList = null;

			while ((count != children.Count) && (tempNodeList == null)) {
				temp = makeNFAFromParseTreeNode (children [count]);
				tempNfa = temp.Item1;
				tempNodeList = temp.Item2;
				nfa += tempNfa;
				count++;
			}
			nodeList = tempNodeList;
			if (nodeList == null) {
				temp = makeNFAFromParseTreeNode (tempNode);
				tempNfa = temp.Item1;
				tempNodeList = temp.Item2;
				nfa += tempNfa;
			} else {
				while (count != children.Count) {
					nodeList.Add (children [count]);
					count++;
				}
				nodeList.Add (tempNode);
			}
			break;
		case Node.Type.TSEQUENCE:
			children = node.GetChildren ();
			count = 0;
			nfa = null;
			nodeList = null;

			foreach (Node child in children) {
				temp = makeNFAFromParseTreeNode (child);
				tempNfa = temp.Item1;
				tempNodeList = temp.Item2;
				if (tempNodeList != null)
					throw new ParserException ("A TVAR type cannot be nested inside a TSEQUENCE type.");
				nfa += tempNfa;
			}
			break;
		case Node.Type.TATOM:
			children = node.GetChildren ();
			if ((children.Count != 1) || (children [0].GetNodeType () != Node.Type.TSEQUENCE))
				throw new ParserException ("TATOM type is not formatted correctly.");
			temp = makeNFAFromParseTreeNode (children [0]);
			nfa = temp.Item1;
			nodeList = temp.Item2;
			break;
		case Node.Type.TASTERISK:
			children = node.GetChildren ();
			if (children.Count != 1)
				throw new ParserException ("TASTERISK type should have one child.");

			temp = makeNFAFromParseTreeNode (children [0]);
			tempNfa = temp.Item1;
			tempNodeList = temp.Item2;
			if (tempNodeList != null)
				throw new ParserException ("A TVAR type cannot be nested inside a TASTERISK type.");
			tempD = tempNfa.GetTransitionFunction ();
			tempFinalState = tempNfa.GetFinalState ();
			nrStates = tempNfa.GetNrStates ();

			d = new Dictionary<State,Dictionary<char,HashSet<State>>> ();
			foreach (State startState in tempD.Keys) {
				nestedD = new Dictionary<char, HashSet<State>> ();
				foreach (char c in tempD[startState].Keys) {
					nestedSet = new HashSet<State> ();
					foreach (State endState in tempD[startState][c])
						nestedSet.Add (endState + 1);
					nestedD.Add (c, nestedSet);
				}
				d.Add (startState + 1, nestedD);
			}
			nestedD = new Dictionary<char, HashSet<State>> ();
			nestedD.Add (Char.MinValue, new HashSet<State> (new State[] { 1, nrStates + 1 }));
			d.Add (0, nestedD);
			nestedD = new Dictionary<char, HashSet<State>> ();
			nestedD.Add (Char.MinValue, new HashSet<State> (new State[] { 1, nrStates + 1 }));
			d.Add (tempFinalState + 1, nestedD);
			finalState = nrStates + 1;
			nfa = new NFA (d, finalState);
			nodeList = null;
			break;
		case Node.Type.TPLUS:
			children = node.GetChildren ();
			if (children.Count != 1)
				throw new ParserException ("TPLUS type should have one child.");

			temp = makeNFAFromParseTreeNode (children [0]);
			tempNfa = temp.Item1;
			tempNodeList = temp.Item2;
			if (tempNodeList != null)
				throw new ParserException ("A TVAR type cannot be nested inside a TPLUS type.");
			tempD = tempNfa.GetTransitionFunction ();
			tempFinalState = tempNfa.GetFinalState ();
			nrStates = tempNfa.GetNrStates ();

			d = new Dictionary<State,Dictionary<char,HashSet<State>>> ();
			foreach (State startState in tempD.Keys) {
				nestedD = new Dictionary<char, HashSet<State>> ();
				foreach (char c in tempD[startState].Keys) {
					nestedSet = new HashSet<State> ();
					foreach (State endState in tempD[startState][c])
						nestedSet.Add (endState + 1);
					nestedD.Add (c, nestedSet);
				}
				d.Add (startState + 1, nestedD);
			}
			nestedD = new Dictionary<char, HashSet<State>> ();
			nestedD.Add (Char.MinValue, new HashSet<State> (new State[] { 1 }));
			d.Add (0, nestedD);
			nestedD = new Dictionary<char, HashSet<State>> ();
			nestedD.Add (Char.MinValue, new HashSet<State> (new State[] { 1, nrStates + 1 }));
			d.Add (tempFinalState + 1, nestedD);
			finalState = nrStates + 1;
			nfa = new NFA (d, finalState);
			nodeList = null;
			break;
		case Node.Type.TQUESTIONMARK:
			children = node.GetChildren ();
			if (children.Count != 1)
				throw new ParserException ("TASTERISK type should have one child.");

			temp = makeNFAFromParseTreeNode (children [0]);
			tempNfa = temp.Item1;
			tempNodeList = temp.Item2;
			if (tempNodeList != null)
				throw new ParserException ("A TVAR type cannot be nested inside a TQUESTIONMARK type.");
			tempD = tempNfa.GetTransitionFunction ();
			tempFinalState = tempNfa.GetFinalState ();
			nrStates = tempNfa.GetNrStates ();

			d = new Dictionary<State,Dictionary<char,HashSet<State>>> ();
			foreach (State startState in tempD.Keys) {
				nestedD = new Dictionary<char, HashSet<State>> ();
				foreach (char c in tempD[startState].Keys) {
					nestedSet = new HashSet<State> ();
					foreach (State endState in tempD[startState][c])
						nestedSet.Add (endState);
					nestedD.Add (c, nestedSet);
				}
				d.Add (startState, nestedD);
			}
			if (d.ContainsKey (0))
				nestedD = d [0];
			else
				nestedD = new Dictionary<char, HashSet<State>> ();
			if (nestedD.ContainsKey (Char.MinValue))
				nestedSet = nestedD [Char.MinValue];
			else
				nestedSet = new HashSet<State> ();
			nestedSet.Add (tempFinalState);
			nestedD [Char.MinValue] = nestedSet;
			d [0] = nestedD;
			finalState = tempFinalState;
			nfa = new NFA (d, finalState);
			nodeList = null;
			break;
		case Node.Type.TCHOICE:
			children = node.GetChildren ();
			d = new Dictionary<State, Dictionary<char, HashSet<State>>> ();
			nestedD = new Dictionary<char, HashSet<State>> ();
			foreach (Node child in children) {
				temp = makeNFAFromParseTreeNode (child);
				tempNfa = temp.Item1;
				tempNodeList = temp.Item2;
				if (tempNodeList != null)
					throw new ParserException ("A TVAR type cannot be nested inside a TCHOICE type.");
				tempD = tempNfa.GetTransitionFunction ();
				tempFinalState = tempNfa.GetFinalState ();
				if (tempFinalState != 1)
					throw new ParserException ("All children of TCHOICE should have only one transition.");
				if ((tempD.Count != 1) || (!tempD.ContainsKey (0)))
					throw new ParserException ("Wrong format for TCHOICE type.");
				foreach (char c in tempD[0].Keys) {
					if (!nestedD.ContainsKey (c))
						nestedD.Add (c, new HashSet<State> (new State[] { 1 }));
				}
			}
			d.Add (0, nestedD);
			finalState = 1;
			nfa = new NFA (d, finalState);
			nodeList = null;
			break;
		case Node.Type.TNO_CHOICE:
			List<char> alphabet = new List<char> ();

			for (char c = 'A'; c <= 'Z'; c++)
				alphabet.Add (c);
			for (char c = 'a'; c <= 'z'; c++)
				alphabet.Add (c);
			for (char c = '0'; c <= '9'; c++)
				alphabet.Add (c);
			alphabet.Add ('_');
			alphabet.Add ('@');

			children = node.GetChildren ();
			foreach (Node child in children) {
				temp = makeNFAFromParseTreeNode (child);
				tempNfa = temp.Item1;
				tempNodeList = temp.Item2;
				if (tempNodeList != null)
					throw new ParserException ("A TVAR type cannot be nested inside a TNO_CHOICE type.");
				tempD = tempNfa.GetTransitionFunction ();
				tempFinalState = tempNfa.GetFinalState ();
				if (tempFinalState != 1)
					throw new ParserException ("All children of TNO_CHOICE should have only one transition.");
				if ((tempD.Count != 1) || (!tempD.ContainsKey (0)))
					throw new ParserException ("Wrong format for TNO_CHOICE type.");
				foreach (char c in tempD[0].Keys)
					if (alphabet.Contains (c))
						alphabet.Remove (c);
			}

			d = new Dictionary<State, Dictionary<char, HashSet<State>>> ();
			nestedD = new Dictionary<char, HashSet<State>> ();
			foreach (char c in alphabet)
				nestedD.Add (c, new HashSet<State> (new State[] { 1 }));
			d.Add (0, nestedD);
			finalState = 1;
			nfa = new NFA (d, finalState);
			nodeList = null;
			break;
		case Node.Type.TUNION:
			children = node.GetChildren ();
			if (children.Count != 2)
				throw new ParserException ("TUNION type should have two children.");

			d = new Dictionary<State, Dictionary<char, HashSet<State>>> ();
			curState = 2;
			d.Add (0, new Dictionary<char, HashSet<State>> ());
			foreach (Node child in children) {
				temp = makeNFAFromParseTreeNode (child);
				tempNfa = temp.Item1;
				tempNodeList = temp.Item2;
				if (tempNodeList != null)
					throw new ParserException ("A TVAR type cannot be nested inside a TUNION type.");
				tempD = tempNfa.GetTransitionFunction ();
				tempFinalState = tempNfa.GetFinalState ();
				nrStates = tempNfa.GetNrStates ();
				foreach (State startState in tempD.Keys) {
					nestedD = new Dictionary<char, HashSet<State>> ();
					foreach (char c in tempD[startState].Keys) {
						nestedSet = new HashSet<State> ();
						foreach (State endState in tempD[startState][c])
							nestedSet.Add (endState + curState);
						nestedD.Add (c, nestedSet);
					}
					d.Add (startState + curState, nestedD);
				}
				if (d [0].ContainsKey (Char.MinValue))
					d [0] [Char.MinValue].Add (curState);
				else
					d [0].Add (Char.MinValue, new HashSet<State> (new State [] { curState }));
				if (!d.ContainsKey (tempFinalState + curState))
					d.Add (tempFinalState + curState, new Dictionary<char, HashSet<State>> ());
				if (d [tempFinalState + curState].ContainsKey (Char.MinValue))
					d [tempFinalState + curState] [Char.MinValue].Add (1);
				else
					d [tempFinalState + curState].Add (Char.MinValue, new HashSet<State> (new State [] { 1 }));
				curState = nrStates + 1;
			}
			nfa = new NFA (d, 1);
			nodeList = null;
			break;
		case Node.Type.TRANGE:
			children = node.GetChildren ();
			if (children.Count != 2)
				throw new ParserException ("TRANGE type should have two children.");

			char start = children [0].GetChar ();
			char end = children [1].GetChar ();

			d = new Dictionary<State, Dictionary<char, HashSet<State>>> ();
			nestedD = new Dictionary<char, HashSet<State>> ();
			for (char c = start; c <= end; c++)
				nestedD.Add (c, new HashSet<State> (new State[] { 1 }));
			d.Add (0, nestedD);
			nfa = new NFA (d, 1);
			nodeList = null;
			break;
		case Node.Type.TVAR:
			nfa = null;
			nodeList = new List<Node> ();
			nodeList.Add (node);
			break;
		case Node.Type.TCHAR:
			d = new Dictionary<State, Dictionary<char, HashSet<State>>> ();
			nestedD = new Dictionary<char, HashSet<State>> ();
			nestedD.Add (node.GetChar (), new HashSet<State> (new State[] { 1 }));
			d.Add (0, nestedD);
			nfa = new NFA (d, 1);
			nodeList = null;
			break;
		case Node.Type.TSLASH:
			d = new Dictionary<State, Dictionary<char, HashSet<State>>> ();
			nestedD = new Dictionary<char, HashSet<State>> ();
			nestedD.Add ('/', new HashSet<State> (new State[] { 1 }));
			d.Add (0, nestedD);
			nfa = new NFA (d, 1);
			nodeList = null;
			break;
		case Node.Type.TLEFT_BRACKET:
			d = new Dictionary<State, Dictionary<char, HashSet<State>>> ();
			nestedD = new Dictionary<char, HashSet<State>> ();
			nestedD.Add ('{', new HashSet<State> (new State[] { 1 }));
			d.Add (0, nestedD);
			nfa = new NFA (d, 1);
			nodeList = null;
			break;
		case Node.Type.TRIGHT_BRACKET:
			d = new Dictionary<State, Dictionary<char, HashSet<State>>> ();
			nestedD = new Dictionary<char, HashSet<State>> ();
			nestedD.Add ('}', new HashSet<State> (new State[] { 1 }));
			d.Add (0, nestedD);
			nfa = new NFA (d, 1);
			nodeList = null;
			break;
		default:
			throw new ParserException ("Uknown node type: " + node.GetNodeType ().ToString ());
		}

		res = new Tuple<NFA, List<Node>> (nfa, nodeList);
		return res;
	}

	static public Tuple<NFA,List<Node>> makeNFAFromParseTreeNodeList (List<Node> nodeList)
	{
		Tuple<NFA,List<Node>> res, temp;
		NFA nfa, tempNfa;
		List<Node> tempNodeList;
		int count = 0;

		if (nodeList == null) {
			res = new Tuple<NFA, List<Node>> (null, null);
			return res;
		}

		nfa = null;
		tempNodeList = null;
		while ((count < nodeList.Count) && (tempNodeList == null)) {
			temp = makeNFAFromParseTreeNode (nodeList [count]);
			tempNfa = temp.Item1;
			tempNodeList = temp.Item2;
			nfa += tempNfa;
			count++;
		}
		while (count < nodeList.Count) {
			tempNodeList.Add (nodeList [count]);
			count++;
		}

		res = new Tuple<NFA, List<Node>> (nfa, tempNodeList);
		return res;
	}

	static public NFA makeNFAFromInteger (int num)
	{
		NFA res;
		Dictionary<State,Dictionary<char,HashSet<State>>> d;
		Dictionary<char,HashSet<State>> nestedD;
		State finalState, curState;
		char[] str;

		str = num.ToString ().ToCharArray ();
		d = new Dictionary<State, Dictionary<char, HashSet<State>>> ();
		curState = 0;
		foreach (char c in str) {
			nestedD = new Dictionary<char, HashSet<State>> ();
			nestedD.Add (c, new HashSet<State> (new State[] { curState+1 }));
			d.Add (curState, nestedD);
			curState++;
		}

		finalState = curState;
		res = new NFA (d, finalState);
		return res;
	}

	static public NFA operator + (NFA nfa1, NFA nfa2)
	{
		if (nfa1 == null)
			return nfa2;

		if (nfa2 == null)
			return nfa1;

		Dictionary<State,Dictionary<char,HashSet<State>>> d =
			new Dictionary<State, Dictionary<char, HashSet<State>>> ();
		Dictionary<State,Dictionary<char,HashSet<State>>> d1 = nfa1.GetTransitionFunction ();
		Dictionary<State,Dictionary<char,HashSet<State>>> d2 = nfa2.GetTransitionFunction ();
		uint nrStates1 = nfa1.GetNrStates ();
		State intermediateState = nfa1.GetFinalState ();
		State finalState = nfa2.GetFinalState () + nrStates1 - 1;
		Dictionary<char,HashSet<State>> nestedD;
		HashSet<State> nestedSet;

		foreach (State startState in d1.Keys) {
			nestedD = new Dictionary<char, HashSet<State>> ();
			foreach (char c in d1[startState].Keys) {
				nestedSet = new HashSet<State> ();
				foreach (State endState in d1[startState][c])
					nestedSet.Add (endState);
				nestedD.Add (c, nestedSet);
			}
			d.Add (startState, nestedD);
		}

		if (d2.ContainsKey (0)) {
			if (d.ContainsKey (intermediateState))
				nestedD = d [intermediateState];
			else
				nestedD = new Dictionary<char, HashSet<State>> ();
			foreach (char c in d2[0].Keys) {
				if (nestedD.ContainsKey (c))
					nestedSet = nestedD [c];
				else
					nestedSet = new HashSet<State> ();
				foreach (State endState in d2[0][c]) {
					if (endState == 0)
						nestedSet.Add (intermediateState);
					else
						nestedSet.Add (endState + nrStates1 - 1);
				}
				nestedD [c] = nestedSet;
			}
			d [intermediateState] = nestedD;
		}

		foreach (State startState in d2.Keys) {
			if (startState == 0)
				continue;
			nestedD = new Dictionary<char, HashSet<State>> ();
			foreach (char c in d2[startState].Keys) {
				nestedSet = new HashSet<State> ();
				foreach (State endState in d2[startState][c]) {
					if (endState == 0)
						nestedSet.Add (intermediateState);
					else
						nestedSet.Add (endState + nrStates1 - 1);
				}
				nestedD.Add (c, nestedSet);
			}
			d.Add (startState + nrStates1 - 1, nestedD);
		}

		return new NFA (d, finalState);
	}

	private HashSet<State> EClosure (State state)
	{
		HashSet<State> states = new HashSet<State> (new State[] { state });

		return EClosure (states);
	}

	private HashSet<State> EClosure (HashSet<State> states)
	{
		HashSet<State> res = new HashSet<State> ();
		List<State> unsearched = new List<State> ();
		State curState;

		foreach (State state in states)
			unsearched.Add (state);

		while (unsearched.Count > 0) {
			curState = unsearched [0];
			res.Add (curState);
			if ((_d.ContainsKey (curState)) && (_d [curState].ContainsKey (Char.MinValue)))
				foreach (State state in _d[curState][Char.MinValue])
					unsearched.Add (state);
			unsearched.Remove (curState);
		}
		return res;
	}

	bool ContainsStates (List<HashSet<State>> Q, HashSet<State> states)
	{
		foreach (HashSet<State> tempStates in Q)
			if (states.SetEquals (tempStates))
				return true;
		return false;
	}

	public DFA ConvertToDFA ()
	{
		Dictionary<State,Dictionary<char,State>> d;
		Dictionary<char,State> nestedD;
		HashSet<State> finalStates;
		Dictionary<HashSet<State>, Dictionary<char,HashSet<State>>> tempD;
		Dictionary<char,HashSet<State>> tempNestedD;
		HashSet<State> tempStartState, curState, tempNestedSet;
		List<HashSet<State>> tempFinalStates;
		HashSet<char> S;
		List<HashSet<State>> Q = new List<HashSet<State>> ();
		List<HashSet<State>> QUnmarked = new List<HashSet<State>> ();
		Dictionary<HashSet<State>,State> transform = new Dictionary<HashSet<State>, State> ();
		State newState;

		S = GetAlphabet ();
		if (S.Contains (Char.MinValue))
			S.Remove (char.MinValue);

		tempD = new Dictionary<HashSet<State>, Dictionary<char, HashSet<State>>> ();
		tempStartState = EClosure (0);
		Q.Add (tempStartState);
		QUnmarked.Add (tempStartState);
		while (QUnmarked.Count > 0) {
			curState = QUnmarked [0];
			tempNestedD = new Dictionary<char, HashSet<State>> ();
			foreach (char c in GetAlphabet()) {
				tempNestedSet = new HashSet<State> ();
				foreach (State startState in curState)
					if ((_d.ContainsKey (startState)) && (_d [startState].ContainsKey (c)))
						foreach (State endState in _d[startState][c])
							tempNestedSet.Add (endState);
				tempNestedSet = EClosure (tempNestedSet);
				if (tempNestedSet.Count == 0)
					continue;
				if (!ContainsStates (Q, tempNestedSet)) {
					QUnmarked.Add (tempNestedSet);
					Q.Add (tempNestedSet);
				}
				tempNestedD.Add (c, tempNestedSet);
			}
			tempD.Add (curState, tempNestedD);
			QUnmarked.RemoveAt (0);
		}
		tempFinalStates = new List<HashSet<State>> ();
		foreach (HashSet<State> states in Q)
			if (states.Contains (GetFinalState ()))
				tempFinalStates.Add (states);

		string res = "";
		res += "S:";
		foreach (char c in _S)
			res += " " + c.ToString ();
		res += Environment.NewLine + "Q:";
		foreach (HashSet<State> sts in Q) {
			res += " {";
			foreach (State st in sts)
				res += " " + st.ToString ();
			res += " } ";
		}
		res += Environment.NewLine + "d:";
		foreach (HashSet<State> states in tempD.Keys) {
			foreach (char c in tempD[states].Keys) {
				res += Environment.NewLine + "({";
				foreach (State state in states)
					res += " " + state.ToString ();
				res += " }," + c.ToString () + ") -> {";
				foreach (State state in tempD [states] [c])
					res += " " + state.ToString ();
				res += " }";
			}
		}
		res += Environment.NewLine + "Start State: {";
		foreach (State state in tempStartState) {
			res += " " + state.ToString ();
			res += " } ";
		}
		res += Environment.NewLine + "Final States :";
		foreach (HashSet<State> states in tempFinalStates) {
			res += Environment.NewLine + "{";
			foreach (State state in states)
				res += " " + state.ToString ();
			res += " }";
		}
		res += Environment.NewLine;

		newState = 0;
		foreach (HashSet<State> oldState in Q) {
			transform.Add (oldState, newState);
			newState++;
		}

		d = new Dictionary<State, Dictionary<char, State>> ();
		foreach (HashSet<State> startState in tempD.Keys) {
			nestedD = new Dictionary<char, State> ();
			foreach (char c in tempD[startState].Keys) {
				tempNestedSet = tempD [startState] [c];
				foreach (HashSet<State> stateSet in transform.Keys) {
					if (stateSet.SetEquals (tempNestedSet))
						nestedD.Add (c, transform [stateSet]);
				}
			}
			tempNestedSet = startState;
			foreach (HashSet<State> stateSet in transform.Keys) {
				if (stateSet.SetEquals (tempNestedSet))
					d.Add (transform [stateSet], nestedD);
			}
		}
		finalStates = new HashSet<State> ();
		foreach (HashSet<State> states in tempFinalStates)
			finalStates.Add (transform [states]);

		return new DFA (d, finalStates);
	}
}

public class Entry
{
	private bool _isVar;
	private string _var;
	private NFA _nfa;

	public Entry (string variable)
	{
		_isVar = true;
		_var = variable;
	}

	public Entry (NFA nfa)
	{
		_isVar = false;
		_nfa = nfa;
	}

	public bool IsVar ()
	{
		return _isVar;
	}

	public string GetVar ()
	{
		return _var;
	}

	public NFA GetNFA ()
	{
		return _nfa;
	}

	public override string ToString ()
	{
		if (_isVar)
			return "VARIABLE " + _var + Environment.NewLine;
		else
			return "NFA:" + Environment.NewLine + _nfa.ToString ();
	}
}
/*public class DependencyModel
{
	private List<DFA> _places;
	private List<string> _transitions;
	private List<List<int>> _inArcs;
	private List<List<int>> _outArcs;

	public DependencyModel (CloudMakefile cloudMakefile)
	{
		_places = cloudMakefile.GetDfas ();
		_transitions = cloudMakefile.GetPolicies ();
		_inArcs = cloudMakefile.GetReverseInputs ();
		_outArcs = cloudMakefile.GetOutputs ();
	}
	/*
	private class Node
	{
		private string[] _filepath;
		private string[] _xmlpath;

		public Node (string[] filepath, string[] xmlpath)
		{
			_filepath = filepath;
			_xmlpath = xmlpath;
		}

		public string[] GetFilepath ()
		{
			return _filepath;
		}

		public string[] GetXmlpath ()
		{
			return _xmlpath;
		}

		public static bool operator == (Node a, Node b)
		{
			// If both are null, or both are same instance, return true.
			if (System.Object.ReferenceEquals (a, b))
				return true;

			// If one is null, but not both, return false.
			if (((object)a == null) || ((object)b == null))
				return false;

			// Return true if the fields match:
			string[] aFilepath = a.GetFilepath ();
			string[] aXmlpath = a.GetXmlpath ();
			string[] bFilepath = b.GetFilepath ();
			string[] bXmlpath = b.GetXmlpath ();

			if (aFilepath.Length != bFilepath.Length)
				return false;

			for (int i = 0; i < aFilepath.Length; i++)
				if (!aFilepath [i].Equals (bFilepath [i]))
					return false;

			if ((aXmlpath == null) && (bXmlpath == null))
				return true;

			if ((aXmlpath == null) || (bXmlpath == null))
				return false;

			if (aXmlpath.Length != bXmlpath.Length)
				return false;

			for (int i = 0; i < aXmlpath.Length; i++)
				if (!aXmlpath [i].Equals (bXmlpath [i]))
					return false;

			return true;
		}

		public static bool operator != (Node a, Node b)
		{
			return !(a == b);
		}

		public override bool Equals (System.Object obj)
		{
			// If parameter is null return false.
			if (obj == null) {
				return false;
			}

			// If parameter cannot be cast to Point return false.
			Node p = obj as Node;
			if ((System.Object)p == null) {
				return false;
			}

			// Return true if the fields match:
			return p == this;
		}

		public bool Equals (Node node)
		{
			return this == node;
		}

		public static string[] patternMatching (Node a, Node b)
		{
			string[] aFilepath = a.GetFilepath ();
			string[] aXmlpath = a.GetXmlpath ();
			string[] bFilepath = b.GetFilepath ();
			string[] bXmlpath = b.GetXmlpath ();

			return null;
		}

		public static void patternMatching (string[] path, string[] pattern)
		{
			string strPath = "";
			string strPattern = "";

			if ((path == null) && (pattern == null))
				return;

			if (path.Length != pattern.Length)
				return;

			if (path.Length != 0) {
				strPath = path [0];
				strPattern = pattern [0].Replace ("*", "([A-Za-z0-9.@]*)");
			}

			for (int i = 1; i < path.Length; i++) {
				strPath += Path.DirectorySeparatorChar + path [i];
				strPattern += Path.DirectorySeparatorChar + pattern [i].Replace ("*", "([A-Za-z0-9.@]*)");
			}
			Console.Out.WriteLine (strPath);
			Console.Out.WriteLine (strPattern);
		}

		public override int GetHashCode ()
		{
			return base.GetHashCode ();
		}

		public override string ToString ()
		{
			string res = "";

			for (int i = 0; i < _filepath.Length; i++) {
				if (i == 0)
					res = _filepath [0];
				else
					res += Path.DirectorySeparatorChar + _filepath [i];
			}

			if (_xmlpath == null)
				return res;

			for (int i = 0; i < _xmlpath.Length; i++)
				res += "{" + _xmlpath [i] + "}";

			return res;
		}
	}
	
	private Dictionary<Node, List<Tuple<Node,string>>> _adjacencyLists;
	private Dictionary<Node, int> _nrParents;

	public DependencyModel ()
	{
		string[] lines = System.IO.File.ReadAllLines ("CloudMakefile");
		bool expectingDependency = true;
		int end, sepIndex;
		string action = null;
		string line;
		string inputLine = null;
		string outputLine = null;
		string[] inputs = null;
		string[] outputs = null;
		Tuple<string[],string[]> inputPaths, outputPaths;
		Node outputNode, inputNode;
		List<Node> inputNodes = new List<Node> ();
		List<Node> outputNodes = new List<Node> ();

		_adjacencyLists = new Dictionary<Node, List<Tuple<Node,string>>> ();
		_nrParents = new Dictionary<Node, int> ();
		for (int i = 0; i < lines.Length; i++) {
			line = lines [i];
			end = line.IndexOf ('#');

			if (end != -1)
				line = line.Substring (0, end);

			if (line == "")
				continue;

			sepIndex = line.IndexOf (':');
			if ((expectingDependency) && (sepIndex != -1)) {
				outputLine = line.Substring (0, sepIndex - 1);
				while (outputLine.StartsWith(" "))
					outputLine = outputLine.Substring (1);
				outputs = outputLine.Split (' ');
				inputLine = line.Substring (sepIndex + 1);
				while (inputLine.StartsWith(" "))
					inputLine = inputLine.Substring (1);
				inputs = inputLine.Split (' ');
				expectingDependency = false;
			} else if ((!expectingDependency) && (line.IndexOf ('\t') == 0)) {
				action = line.Substring (1);
				if (action.Contains ("$(inputs)"))
					Console.Out.WriteLine ("Found inputs");
				action = action.Replace ("$(inputs)", inputLine);
				action = action.Replace ("$(outputs)", outputLine);
				expectingDependency = true;
				Console.Out.WriteLine ("Inputs:");
				for (int j = 0; j < inputs.Length; j++)
					Console.Out.WriteLine (inputs [j]);
				Console.Out.WriteLine ("Outputs:");
				for (int j = 0; j < outputs.Length; j++)
					Console.Out.WriteLine (outputs [j]);
				Console.Out.WriteLine ("Action: " + action);
				Console.Out.WriteLine ();
				inputNodes.Clear ();
				outputNodes.Clear ();
				for (int j = 0; j < inputs.Length; j++) {
					inputPaths = GetPaths (inputs [j]);
					inputNode = new Node (inputPaths.Item1, inputPaths.Item2);
					inputNodes.Add (inputNode);
					if (!_adjacencyLists.ContainsKey (inputNode)) {
						Console.Out.WriteLine ("Tested Node: " + inputNode);
						Console.Out.WriteLine (this.ToString ());
						_adjacencyLists.Add (inputNode, new List<Tuple<Node, string>> ());
						_nrParents [inputNode] = 0;
					}
				}

				for (int j = 0; j < outputs.Length; j++) {
					outputPaths = GetPaths (outputs [j]);
					outputNode = new Node (outputPaths.Item1, outputPaths.Item2);
					outputNodes.Add (outputNode);
					if (!_adjacencyLists.ContainsKey (outputNode)) {
						Console.Out.WriteLine ("Tested Node: " + outputNode);
						Console.Out.WriteLine (this.ToString ());
						_adjacencyLists.Add (outputNode, new List<Tuple<Node, string>> ());
						_nrParents [outputNode] = 0;
					}
				}

				for (int j = 0; j < inputNodes.Count; j++) {
					for (int k = 0; k < outputNodes.Count; k++) {
						_adjacencyLists [inputNodes [j]].Add (new Tuple<Node, string> (outputNodes [k], action));
						_nrParents [outputNodes [k]] ++;
					}
				}
			}
		}
	}

	public override string ToString ()
	{
		string res = "Dependency Graph" + Environment.NewLine + "----------------" + Environment.NewLine;

		foreach (Node node in _adjacencyLists.Keys) {
			res += node.ToString () + " : ";
			foreach (Tuple<Node, string> tuple in _adjacencyLists[node])
				res += tuple.Item1.ToString () + " (" + tuple.Item2 + ") ";
			res += " : " + _nrParents [node].ToString ();
			res += Environment.NewLine;
		}

		return res;
	}

	static private Tuple<string[],string[]> GetPaths (string str)
	{
		string[] filepath = null;
		string[] xmlpath = null;
		int sep;

		sep = str.IndexOf ('{');
		if (sep == -1) {
			filepath = str.Split (Path.DirectorySeparatorChar);
		} else {
			filepath = str.Substring (0, sep).Split (Path.DirectorySeparatorChar);
			xmlpath = str.Substring (sep).Split (new char[2] { '{', '}' });
		}

		return new Tuple<string[], string[]> (filepath, xmlpath);
	}
}*/
public class Node
{
	private Type _type;
	private char _c;
	private List<Node> _children;

	public enum Type
	{
		TVAR,
		TCHAR,
		TRANGE,
		TCHOICE,
		TNO_CHOICE,
		TUNION,
		TATOM,
		TASTERISK,
		TPLUS,
		TQUESTIONMARK,
		TSEQUENCE,
		TFILEPATH,
		TXMLPATH,
		TENTRY,
		TENTRIES,
		TRULE,
		TSRULE,
		TNRULE,
		TACTION,
		TSLASH,
		TLEFT_BRACKET,
		TRIGHT_BRACKET,
	}
	;

	public Node (string nodeString)
	{
		int start = nodeString.IndexOf ('(');
		int end = nodeString.LastIndexOf (')');
		string typeString = nodeString.Substring (0, start);
		string contentString = nodeString.Substring (start + 1, end - start - 1);
		List<string> childrenStrings;

		if (typeString == "CHAR") {
			_type = Type.TCHAR;
			if (contentString.Length != 1)
				throw new ParserException ("Wrong Input to CHAR Node.");
			_c = contentString [0];
			_children = null;
		} else {
			childrenStrings = SplitContent (contentString);
			switch (typeString) {
			case("VAR"):
				_type = Type.TVAR;
				break;
			case("RANGE"):
				_type = Type.TRANGE;
				if (childrenStrings.Count != 2)
					throw new ParserException ("Wrong Number of Inputs to RANGE Node.");
				break;
			case("CHOICE"):
				_type = Type.TCHOICE;
				break;
			case("NO_CHOICE"):
				_type = Type.TNO_CHOICE;
				break;
			case("UNION"):
				_type = Type.TUNION;
				if (childrenStrings.Count != 2)
					throw new ParserException ("Wrong Number of Inputs to UNION Node.");
				break;
			case("ATOM"):
				_type = Type.TATOM;
				if (childrenStrings.Count != 1)
					throw new ParserException ("Wrong Number of Inputs to TATOM Node.");
				break;
			case("ASTERISK"):
				_type = Type.TASTERISK;
				if (childrenStrings.Count != 1)
					throw new ParserException ("Wrong Number of Inputs to ASTERISK Node.");
				break;
			case("PLUS"):
				_type = Type.TPLUS;
				if (childrenStrings.Count != 1)
					throw new ParserException ("Wrong Number of Inputs to PLUS Node.");
				break;
			case("QUESTIONMARK"):
				_type = Type.TQUESTIONMARK;
				if (childrenStrings.Count != 1)
					throw new ParserException ("Wrong Number of Inputs to QUESTIONMARK Node.");
				break;
			case("SEQUENCE"):
				_type = Type.TSEQUENCE;
				break;
			case("FILEPATH"):
				_type = Type.TFILEPATH;
				break;
			case("XMLPATH"):
				_type = Type.TXMLPATH;
				break;
			case("ENTRY"):
				_type = Type.TENTRY;
				if ((childrenStrings.Count != 2) && (childrenStrings.Count != 1))
					throw new ParserException ("Wrong Number of Inputs to ENTRY Node.");
				break;
			case("ENTRIES"):
				_type = Type.TENTRIES;
				break;
			case ("RULE"):
				_type = Type.TRULE;
				if (childrenStrings.Count != 3)
					throw new ParserException ("Wrong Number of Inputs to RULE Node.");
				break;
			case("SRULE"):
				_type = Type.TSRULE;
				if (childrenStrings.Count != 3)
					throw new ParserException ("Wrong Number of Inputs to SRULE Node.");
				break;
			case("NRULE"):
				_type = Type.TNRULE;
				if (childrenStrings.Count != 3)
					throw new ParserException ("Wrong Number of Inputs to NRULE Node.");
				break;
			case("ACTION"):
				_type = Type.TACTION;
				break;
			case("SLASH"):
				_type = Type.TSLASH;
				if (childrenStrings.Count != 0)
					throw new ParserException ("Wrong Number of Inputs to SLASH Node.");
				break;
			case("LEFT_BRACKET"):
				_type = Type.TLEFT_BRACKET;
				if (childrenStrings.Count != 0)
					throw new ParserException ("Wrong Number of Inputs to SLASH Node.");
				break;
			case("RIGHT_BRACKET"):
				_type = Type.TRIGHT_BRACKET;
				if (childrenStrings.Count != 0)
					throw new ParserException ("Wrong Number of Inputs to SLASH Node.");
				break;
			default:
				throw new ParserException (typeString + " is a Wrong Type of Node");
			}
			_children = new List<Node> ();
			for (int i = 0; i < childrenStrings.Count; i++)
				_children.Add (new Node (childrenStrings [i]));
		}
	}

	private List<string> SplitContent (string contentString)
	{
		List<string> res = new List<string> ();
		int countLeftPar = 0;
		int countRightPar = 0;
		int start = 0;
		int i = 0;
		string temp;

		while (i != contentString.Length) {
			if ((i >= 5) && (contentString.Substring (i - 5, 5) == "CHAR(")) {
				i++;
				continue;
			}
			if (contentString [i] == '(')
				countLeftPar += 1;
			else if (contentString [i] == ')')
				countRightPar += 1;
			if ((countLeftPar != 0) && (countLeftPar == countRightPar)) {
				temp = contentString.Substring (start, i - start + 1);
				res.Add (temp);
				countLeftPar = 0;
				countRightPar = 0;
				start = i + 2;
			}
			i++;
		}
		return res;
	}

	public Type GetNodeType ()
	{
		return _type;
	}

	public char GetChar ()
	{
		return _c;
	}

	public List<Node> GetChildren ()
	{
		return _children;
	}

	public override string ToString ()
	{
		string res;

		res = _type.ToString () + "(";
		if (_type == Type.TCHAR)
			res += _c.ToString ();
		else {
			if (_children.Count != 0) {
				for (int i = 0; i < _children.Count-1; i++)
					res += _children [i].ToString () + ",";
				res += _children [_children.Count - 1].ToString ();
			}
		}
		res += ")";

		return res;
	}
}

public class CloudMakefile
{
	private List<List<int>> _inputs;
	private List<List<int>> _outputs;
	private List<List<int>> _reverseInputs;
	private List<List<int>> _reverseOutputs;
	private List<DFA> _dfas;
	private List<string> _policies;
	private List<HashSet<int>> _order;

	[DllImport ("libparser")]
	public static extern int yywrap ();
	
	[DllImport ("libparser")]
	public static extern void Parse ();

	public CloudMakefile ()
	{
		// ProcessStartInfo parserInfo = new ProcessStartInfo ();
		string parseTreeFilename = "CloudMakefile.parseTree";
		string[] lines;
		Node rootNode;
		int currentRule;

		// Count time of static analysis from here.
		Stopwatch stopwatch = new Stopwatch ();
		stopwatch.Start ();

		// Run parser and create parse tree.
		Parse ();

		// Initiate variables.
		_inputs = new List<List<int>> ();
		_outputs = new List<List<int>> ();
		_reverseInputs = new List<List<int>> ();
		_reverseOutputs = new List<List<int>> ();
		_dfas = new List<DFA> ();
		_policies = new List<string> ();
		_order = new List<HashSet<int>> ();

		// Start Encoding to Entries(NFAs).
		Console.Out.WriteLine ("Starting transformation process.");
		lines = File.ReadAllLines (@parseTreeFilename);
		currentRule = 0;
		foreach (string line in lines) {
			rootNode = new Node (line);
			currentRule = ParseFRule (rootNode, currentRule, new Dictionary<string, List<NFA>> ());
		}

		if (DetectCycles ())
			throw new Exception ("CloudMakefile: There are cyclic dependencies in CloudMakefile");
		stopwatch.Stop ();

		Console.Out.WriteLine ("Static Analysis Completed in: " + stopwatch.ElapsedMilliseconds.ToString () + "ms");
	}

	public List<DFA> GetDFAs ()
	{
		return _dfas;
	}

	public List<List<int>> GetInputs ()
	{
		return _inputs;
	}

	public List<List<int>> GetOutputs ()
	{
		return _outputs;
	}

	public List<List<int>> GetReverseInputs ()
	{
		return _reverseInputs;
	}

	public List<List<int>> GetReverseOutputs ()
	{
		return _reverseOutputs;
	}

	public List<string> GetPolicies ()
	{
		return _policies;
	}

	public List<HashSet<int>> GetOrder ()
	{
		return _order;
	}

	public override string ToString ()
	{
		string res = "CLOUDMAKEFILE:"+ Environment.NewLine + Environment.NewLine;

		for (int curRule = 0; curRule < _inputs.Count; curRule++) {
			res += "Rule " + curRule.ToString () + Environment.NewLine;
			res += "Inputs:" + Environment.NewLine;
			foreach (int i in _inputs[curRule])
				res += _dfas [i].ToString ();
			res += "Outputs:" + Environment.NewLine;
			foreach (int i in _outputs[curRule])
				res += _dfas [i].ToString ();
			res += "Action: " + _policies [curRule] + Environment.NewLine + Environment.NewLine;
		}

		res += "Order:" + Environment.NewLine;
		for (int i = 0; i < _order.Count; i++) {
			res += i.ToString () + ":";
			foreach (int tr in _order[i])
				res += " " + tr.ToString ();
			res += Environment.NewLine;
		}
		res += Environment.NewLine;

		return res;
	}

	private int ParseFRule (Node fruleNode, int currentRule, Dictionary<string,List<NFA>> vars)
	{
		Tuple<NFA,List<Node>> temp;
		List<Node> tempNodeList;
		Node.Type type = fruleNode.GetNodeType ();
		List<Node> children, grandchildren;
		String variable;
		NFA tempNfa;
		List<NFA> nfaList;
		int start, end;
		Dictionary<string,List<NFA>> tempVars;
		int nextRule = currentRule;

		switch (type) {
		case(Node.Type.TSRULE):
			children = fruleNode.GetChildren ();
			variable = ParseString (children [0]);
			nfaList = new List<NFA> ();
			grandchildren = children [1].GetChildren ();
			for (int i = 1; i < grandchildren.Count; i++) {
				if (grandchildren [i].GetNodeType () != Node.Type.TSEQUENCE)
					throw new ParserException ("TSRULE type should only have TSEQUENCE types after TVAR type.");
				temp = NFA.makeNFAFromParseTreeNode (grandchildren [i]);
				tempNfa = temp.Item1;
				tempNodeList = temp.Item2;
				if (tempNodeList != null)
					throw new ParserException ("There should not be variables inside the definition of other variables.");
				nfaList.Add (tempNfa);
			}
			if (vars.ContainsKey (variable))
				throw new ParserException ("There should not be two variables with the same name within the same rule.");
			tempVars = new Dictionary<string, List<NFA>> (vars);
			tempVars.Add (variable, nfaList);
			nextRule = ParseFRule (children [2], currentRule, tempVars);
			break;
		case(Node.Type.TNRULE):
			children = fruleNode.GetChildren ();
			variable = ParseString (children [0]);
			nfaList = new List<NFA> ();
			grandchildren = children [1].GetChildren ();
			if (children [1].GetChildren ().Count != 2)
				throw new ParserException ("TNRULE should have two children under TSEQUENCE TYPE.");
			start = Int32.Parse (ParseString (grandchildren [0]));
			end = Int32.Parse (ParseString (grandchildren [1]));
			for (int i = start; i <= end; i++)
				nfaList.Add (NFA.makeNFAFromInteger (i));
			tempVars = new Dictionary<string, List<NFA>> (vars);
			tempVars.Add (variable, nfaList);
			nextRule = ParseFRule (children [2], currentRule, tempVars);
			break;
		case(Node.Type.TRULE):
			nextRule = ParseRule (fruleNode, currentRule, vars);
			break;
		default:
			throw new ParserException ("The rule must be of type TSRULE, TNRULE or TRULE.");
		}
		return nextRule;
	}

	private DFA SubstituteEntries (List<Entry> entries, Dictionary<string, NFA> vars)
	{
		List<NFA> nfas = new List<NFA> ();
		NFA nfa = null;

		foreach (Entry entry in entries) {
			if (entry.IsVar ()) {
				if (!vars.ContainsKey (entry.GetVar ()))
					throw new ParserException ("Variable " + entry.GetVar () + " is not defined.");
				nfas.Add (vars [entry.GetVar ()]);
			} else {
				nfas.Add (entry.GetNFA ());
			}
		}

		foreach (NFA tempNfa in nfas)
			nfa += tempNfa;

		return nfa.ConvertToDFA ();
	}

	private static DFA Reduce (DFA dfa)
	{
		DFA res = dfa;

		if (res != null)
			res = res.RemoveRedudantStates ();
		if (res != null)
			res = res.Minimize ();

		return res;
	}

	private void AddDFA (DFA newDfa, bool isInput, int currentRule)
	{
		DFA tempDfa = null;
		DFA dfa = null;
		int nextDfa;

		for (int i = 0; i < _dfas.Count; i++) {
			tempDfa = _dfas [i];
			dfa = newDfa * tempDfa;
			dfa = Reduce (dfa);
			if (dfa != null) {
				_dfas [i] = dfa;
				if (isInput) {
					_inputs [currentRule].Add (i);
					_reverseInputs [i].Add (currentRule);
				} else {
					_outputs [currentRule].Add (i);
					_reverseOutputs [i].Add (currentRule);
				}
				dfa = tempDfa - newDfa;
				dfa = Reduce (dfa);
				if (dfa != null) {
					nextDfa = _dfas.Count;
					_dfas.Add (dfa);
					_reverseInputs.Add (new List<int> (_reverseInputs [i]));
					_reverseInputs [nextDfa].Remove (currentRule);
					_reverseOutputs.Add (new List<int> (_reverseInputs [i]));
					_reverseOutputs [nextDfa].Remove (currentRule);
					foreach (int rule in _reverseInputs[nextDfa])
						_inputs [rule].Add (nextDfa);
					foreach (int rule in _reverseOutputs[nextDfa])
						_outputs [rule].Add (nextDfa);
				}
				break;
			}
			tempDfa = null;
		}

		dfa = newDfa - tempDfa;
		dfa = Reduce (dfa);
		if (dfa != null) {
			nextDfa = _dfas.Count;
			_dfas.Add (dfa);
			_reverseInputs.Add (new List<int> ());
			_reverseOutputs.Add (new List<int> ());
			if (isInput) {
				_inputs [currentRule].Add (nextDfa);
				_reverseInputs [nextDfa].Add (currentRule);
			} else {
				_outputs [currentRule].Add (nextDfa);
				_reverseOutputs [nextDfa].Add (currentRule);
			}
		}
	}

	private void ParseSingleRule (List<List<Entry>> inputEntries, List<List<Entry>> outputEntries, string policy,
	                              int currentRule, Dictionary<string, NFA> vars)
	{
		_inputs.Add (new List<int> ());
		_outputs.Add (new List<int> ());

		foreach (List<Entry> entries in inputEntries)
			AddDFA (SubstituteEntries (entries, vars), true, currentRule);

		foreach (List<Entry> entries in outputEntries)
			AddDFA (SubstituteEntries (entries, vars), false, currentRule);

		_policies.Add (policy);
	}

	private int ParseMultipleRule (List<List<Entry>> inputEntries, List<List<Entry>> outputEntries, string policy,
	                               int currentRule, Dictionary<string, List<NFA>> mVars,
	                               Dictionary<string,NFA> vars)
	{
		Dictionary<string,List<NFA>> tempMVars = new Dictionary<string, List<NFA>> (mVars);
		int nextRule = currentRule;

		if (tempMVars.Count == 0) {
			ParseSingleRule (inputEntries, outputEntries, policy, nextRule, vars);
			nextRule++;
		} else {
			IEnumerator enumerator = tempMVars.Keys.GetEnumerator ();
			enumerator.MoveNext ();
			string variable = (string)enumerator.Current;
			List<NFA> nfaList = tempMVars [variable];

			tempMVars.Remove (variable);
			foreach (NFA tempNfa in nfaList) {
				Dictionary<string, NFA> tempVars = new Dictionary<string, NFA> (vars);
				if (tempVars.ContainsKey (variable))
					throw new ParserException ("Variable " + variable + " is defined more than once.");
				tempVars.Add (variable, tempNfa);
				nextRule = ParseMultipleRule (inputEntries, outputEntries, policy, nextRule, tempMVars, tempVars);
			}
		}

		return nextRule;
	}

	private int ParseRule (Node ruleNode, int currentRule, Dictionary<string,List<NFA>> cVars)
	{
		List<Node> children = ruleNode.GetChildren ();
		List<List<Entry>> inputEntries = new List<List<Entry>> ();
		List<List<Entry>> outputEntries = new List<List<Entry>> ();
		String policy;

		foreach (Node node in children[0].GetChildren()) {
			if (node.GetNodeType () != Node.Type.TENTRY)
				throw new ParserException ("The children of TENTRIES node should be of TENTRY type.");
			outputEntries.Add (ParseEntry (node));
		}
		foreach (Node node in children[1].GetChildren()) {
			if (node.GetNodeType () != Node.Type.TENTRY)
				throw new ParserException ("The children of TENTRIES node should be of TENTRY type.");
			inputEntries.Add (ParseEntry (node));
		}
		policy = ParseString (children [2]);

		return ParseMultipleRule (inputEntries, outputEntries, policy, currentRule, cVars,
		                          new Dictionary<string, NFA> ());
	}

	public List<Entry> ParseEntry (Node node)
	{
		Tuple<NFA,List<Node>> tuple;
		NFA nfa;
		List<Node> nodeList;
		List<Entry> res = new List<Entry> ();

		nodeList = new List<Node> ();
		nodeList.Add (node);
		tuple = NFA.makeNFAFromParseTreeNode (node);
		nfa = tuple.Item1;
		nodeList = tuple.Item2;

		if (nfa != null)
			res.Add (new Entry (nfa));
		while (nodeList != null) {
			if (nodeList [0].GetNodeType () != Node.Type.TVAR)
				throw new ParserException ("You can only encounter TVAR type here.");
			res.Add (new Entry (ParseString (nodeList [0])));
			nodeList.RemoveAt (0);
			tuple = NFA.makeNFAFromParseTreeNodeList (nodeList);
			nfa = tuple.Item1;
			nodeList = tuple.Item2;
			if (nfa != null)
				res.Add (new Entry (nfa));
		}

		return res;
	}

	static public string ParseString (Node node)
	{
		List<Node> children = node.GetChildren ();
		string res = "";

		foreach (Node charNode in children) {
			if (charNode.GetNodeType () != Node.Type.TCHAR)
				throw new ParserException ("The string must consist of TCHAR nodes.");
			res += charNode.GetChar ().ToString ();
		}
		return res;
	}

	public class ParseException : Exception
	{
		public ParseException ()
		{
		}

		public ParseException (string message)
			: base(message)
		{
		}

		public ParseException (string message, Exception inner)
			: base(message, inner)
		{
		}
	}
	/*
	public void Include (string filename)
	{
		if (_entriesToDFAs.ContainsKey (filename))
			return;

		_entriesToDFAs.Add (filename, new HashSet<int> ());
		for (int i = 0; i < _dfas.Count; i++) {
			FA.Response response = _dfas [i].Parse (filename);

			if (response == FA.Response.ACCEPT) {
				_entriesToDFAs [filename].Add (i);
				if (!_DFAsToEntries.ContainsKey (i))
					_DFAsToEntries.Add (i, new HashSet<string> ());
				_DFAsToEntries [i].Add (filename);
				return;
			} else if (response == FA.Response.NOT_REJECT) {
				if (filename.EndsWith (".xml")) {
					List<Tuple<XmlElement,string>> unsearchedNodes = new List<Tuple<XmlElement, string>> ();
					XmlDocument doc = new XmlDocument ();

					doc.Load (filename);
					unsearchedNodes.Add (
						new Tuple<XmlElement, string> (doc.DocumentElement,
					                                filename + "{" + doc.DocumentElement.Name + "}"));
					while (unsearchedNodes.Count > 0) {
						XmlElement curElement = unsearchedNodes [0].Item1;
						string curName = unsearchedNodes [0].Item2;
						FA.Response curResponse = _dfas [i].Parse (curName);

						if (curResponse == FA.Response.ACCEPT) {
							_entriesToDFAs [filename].Add (i);
							if (!_DFAsToEntries.ContainsKey (i))
								_DFAsToEntries.Add (i, new HashSet<string> ());
							_DFAsToEntries [i].Add (filename);
						} else if (curResponse == FA.Response.NOT_REJECT) {
							foreach (XmlElement childElement in curElement.ChildNodes) {
								string childName = curName + "{" + childElement.Name + "}";

								unsearchedNodes.Add (new Tuple<XmlElement, string> (childElement, childName));
							}
						}
						unsearchedNodes.RemoveAt (0);
					}
				}
				return;
			}
		}
	}
	*/
	private bool DetectCycles ()
	{
		HashSet<int> availableTransitions = new HashSet<int> ();
		int oldCount;

		Console.Out.WriteLine ("Started Cyclic Dependencies Detection");
		for (int i = 0; i < _inputs.Count; i++)
			availableTransitions.Add (i);
		oldCount = availableTransitions.Count + 1;
		while ((availableTransitions.Count > 0) && (availableTransitions.Count < oldCount)) {
			HashSet<int> aggOutputs = new HashSet<int> ();
			HashSet<int> firstOrder = new HashSet<int> ();

			oldCount = availableTransitions.Count;
			foreach (int tr in availableTransitions) {
				foreach (int output in _outputs[tr])
					aggOutputs.Add (output);
			}
			foreach (int tr in availableTransitions) {
				bool flag = true;

				foreach (int input in _inputs[tr]) {
					if (aggOutputs.Contains (input)) {
						flag = false;
						break;
					}
				}
				if (flag)
					firstOrder.Add (tr);
			}
			_order.Add (firstOrder);
			foreach (int tr in firstOrder)
				availableTransitions.Remove (tr);
		}

		if (availableTransitions.Count > 0)
			return true;

		return false;
	}

	public List<Tuple<string,int>> FindMatchingEntries (string entry)
	{
		HashSet<int> dfaList = new HashSet<int> ();

		Console.Out.WriteLine ("Find matching entries for " + entry);
		for (int i = 0; i < _dfas.Count; i++)
			dfaList.Add (i);

		return FindMatchingEntries (entry, dfaList);
	}

	public List<Tuple<string,int>> FindMatchingEntries (string entry, HashSet<int> dfaList)
	{
		List<Tuple<string,int>> res = new List<Tuple<string, int>> ();
		List<Tuple<string, HashSet<int>>> unexplored = new List<Tuple<string, HashSet<int>>> ();

		unexplored.Add (new Tuple<string, HashSet<int>> (entry, dfaList));
		while (unexplored.Count != 0) {
			string curPath = unexplored [0].Item1;
			HashSet<int> curDfaList = unexplored [0].Item2;
			HashSet<int> tempDfaList = new HashSet<int> ();
			HashSet<string> children = new HashSet<string> ();

			if (curPath.EndsWith (".xml")) {
				XmlDocument doc = new XmlDocument ();

				doc.Load (curPath);
				children.Add (curPath + "{" + doc.DocumentElement.Name + "}");
			} else if (curPath.Contains (".xml")) {
				int index = curPath.IndexOf (".xml") + 4;
				string filepath = curPath.Substring (0, index);
				string xmlpath = curPath.Substring (index);
				string[] xmlFields = xmlpath.Split ('{', '}');
				XmlDocument doc = new XmlDocument ();
				string xpath = xmlFields [0];
				XmlNodeList xmlNodeList;

				for (int i = 1; i < xmlFields.Length; i++)
					xpath += '/' + xmlFields [i];
				doc.Load (filepath);
				xmlNodeList = doc.SelectNodes (xpath);

				foreach (XmlNode node in xmlNodeList) {
					if (node.HasChildNodes)
						foreach (XmlNode childNode in node.ChildNodes)
							children.Add (curPath + "{" + childNode.Name + "}");
				}
			} else if (Directory.Exists (curPath) || curPath == "") {
				string path = (curPath == "") ? Directory.GetCurrentDirectory () : curPath;
				DirectoryInfo curDir = new DirectoryInfo (path);
				FileInfo[] files = curDir.GetFiles ("*", SearchOption.TopDirectoryOnly);
				DirectoryInfo[] dirs = curDir.GetDirectories ("*", SearchOption.TopDirectoryOnly);

				path = (curPath == "") ? curPath : curPath + '/';
				foreach (FileInfo file in files)
					children.Add (path + file.Name);
				foreach (DirectoryInfo dir in dirs)
					children.Add (path + dir.Name);
			}

			foreach (int i in dfaList) {
				FA.Response response = _dfas [i].Parse (curPath);

				switch (response) {
				case FA.Response.ACCEPT:
					res.Add (new Tuple<string, int> (curPath, i));
					break;
				case FA.Response.NOT_REJECT:
					tempDfaList.Add (i);
					break;
				case FA.Response.REJECT:
					break;
				}
			}

			if (tempDfaList.Count != 0)
				foreach (string child in children)
					unexplored.Add (new Tuple<string, HashSet<int>> (child, tempDfaList));

			unexplored.RemoveAt (0);
		}

		return res;
	}
}

public class DependencyStructure
{
	private CloudMakefile _cloudMakefile;
	private List<string> _entries;
	private List<string> _policies;
	private List<List<int>> _inputs;
	private List<List<int>> _outputs;
	private List<List<int>> _reverseInputs;
	private List<List<int>> _reverseOutputs;
	private HashSet<int> _tokens;
	private List<HashSet<int>> _order;
	private HashSet<string> _configFiles;
	private HashSet<string> _cloudMakeConfigFiles;
	// Constructor of DependencyStructure class.
	public DependencyStructure (CloudMakefile cloudMakefile)
	{
		_cloudMakefile = cloudMakefile;
		_entries = new List<string> ();
		_policies = cloudMakefile.GetPolicies ();
		_inputs = new List<List<int>> ();
		_outputs = new List<List<int>> ();
		for (int i = 0; i < _policies.Count; i++) {
			_inputs.Add (new List<int> ());
			_outputs.Add (new List<int> ());
		}
		_reverseInputs = new List<List<int>> ();
		_reverseOutputs = new List<List<int>> ();
		_tokens = new HashSet<int> ();
		_order = cloudMakefile.GetOrder ();
		_configFiles = new HashSet<string> ();
		_cloudMakeConfigFiles = new HashSet<string> ();
	}
	// Transformation of DependencyStructure object to string.
	public override string ToString ()
	{
		string res = "DEPENDENCY_STRUCTURE:" + Environment.NewLine + Environment.NewLine;

		for (int curRule = 0; curRule < _inputs.Count; curRule++) {
			res += "Rule " + curRule.ToString () + Environment.NewLine;
			res += "Inputs:" + Environment.NewLine;
			foreach (int i in _inputs[curRule])
				res += _entries [i].ToString () + Environment.NewLine;
			res += "Outputs:" + Environment.NewLine;
			foreach (int i in _outputs[curRule])
				res += _entries [i].ToString () + Environment.NewLine;
			res += "Action: " + _policies [curRule] + Environment.NewLine + Environment.NewLine;
		}

		res += "Order:" + Environment.NewLine;
		for (int i = 0; i < _order.Count; i++) {
			res += i.ToString () + ":";
			foreach (int tr in _order[i])
				res += " " + tr.ToString ();
			res += Environment.NewLine;
		}
		res += Environment.NewLine;

		res += "Tokens:" + Environment.NewLine;
		foreach (int token in _tokens) {
			res += _entries [token] + Environment.NewLine;
		}
		res += Environment.NewLine;

		res += "Config Files:" + Environment.NewLine;
		foreach (string configFile in _configFiles) {
			res += configFile + Environment.NewLine;
		}
		res += Environment.NewLine;

		res += "CloudMake Config Files:" + Environment.NewLine;
		foreach (string cloudMakeConfigFile in _cloudMakeConfigFiles) {
			res += cloudMakeConfigFile + Environment.NewLine;
		}
		res += Environment.NewLine;

		return res;
	}

	public HashSet<string> GetConfigFiles ()
	{
		return _configFiles;
	}

	public HashSet<string> GetCloudMakeConfigFiles ()
	{
		return _cloudMakeConfigFiles;
	}
	// Adds entries to dependency structure.
	public void AddEntries (List<Tuple<string,int>> entries)
	{
		foreach (Tuple<string,int> entry in entries)
			AddEntry (entry);
	}
	// Adds entry to dependency structure.
	public void AddEntry (Tuple<string,int> entry)
	{
		string entryname = entry.Item1;
		int i = entry.Item2;
		List<int> reverseInputs = _cloudMakefile.GetReverseInputs () [i];
		List<int> reverseOutputs = _cloudMakefile.GetReverseOutputs () [i];
		int entryIndex = _entries.Count;

		_entries.Add (entryname);
		_reverseInputs.Add (new List<int> (reverseInputs));
		_reverseOutputs.Add (new List<int> (reverseOutputs));
		foreach (int policyIndex in reverseInputs)
			_inputs [policyIndex].Add (entryIndex);
		foreach (int policyIndex in reverseOutputs)
			_outputs [policyIndex].Add (entryIndex);
	}
	// Adds token to the specified entry.
	public void AddTokenToEntry (string entryname)
	{
		int entryIndex = _entries.IndexOf (entryname);

		_tokens.Add (entryIndex);
	}
	// Adds a new configuration file.
	public void AddConfigFile (string configFilename)
	{
		_configFiles.Add (configFilename);
	}
	// Adds a new CloudMake configuration file.
	public void AddCloudMakeConfigFile (string configFilename)
	{
		_cloudMakeConfigFiles.Add (configFilename);
	}
	// Helper function.
	private string XmlNodeListToString (XmlNodeList nodeList)
	{
		string returnStr = "";

		if (nodeList != null) {
			foreach (XmlNode node in nodeList) {
				returnStr += node.OuterXml;
			}

		}
		return "<Root>" + returnStr + "</Root>";
	}
	// Helper function.
	private string HashEntry (string entryname)
	{
		if (entryname.EndsWith (".xml")) {
			return CloudMake.GetHash (CloudMake.GetXMLAsString (entryname));
		} else {
			int index = entryname.IndexOf (".xml") + 4;
			string filepath = entryname.Substring (0, index);
			string xmlpath = entryname.Substring (index);
			string[] xmlFields = xmlpath.Split ('{', '}');
			XmlDocument doc = new XmlDocument ();
			string xpath = xmlFields [0];
			XmlNodeList xmlNodeList;

			for (int k = 1; k < xmlFields.Length; k++)
				xpath += '/' + xmlFields [k];
			doc.Load (filepath);
			xmlNodeList = doc.SelectNodes (xpath);
			return CloudMake.GetHash (XmlNodeListToString (xmlNodeList));
		}
	}

	private string MatchTokenToConfigFile (string entryname)
	{
		foreach (string configFilename in _configFiles)
			if (entryname.StartsWith (configFilename))
				return configFilename;
		return null;
	}

	private string MatchTokenToCloudMakeConfigFile (string entryname)
	{
		foreach (string cloudMakeConfigFilename in _cloudMakeConfigFiles)
			if (entryname.StartsWith (cloudMakeConfigFilename))
				return cloudMakeConfigFilename;
		return null;
	}
	// Run CloudMake
	public Tuple<HashSet<string>,HashSet<string>> Run ()
	{
		HashSet<string> cloudMakeConfigFiles = new HashSet<string> ();
		HashSet<string> configFiles = new HashSet<string> ();
		List<List<int>> outputs = _cloudMakefile.GetOutputs ();

		Console.Out.WriteLine ("Started Running CloudMake.");
		for (int i = 0; i < _order.Count; i++) {
			Console.Out.WriteLine ("Order: " + i.ToString ());

			HashSet<int> activeTransitions = new HashSet<int> ();
			HashSet<int> tokensToRemove = new HashSet<int> ();
			HashSet<int> tokensToAdd = new HashSet<int> ();

			// Find active transitions.
			Console.Out.WriteLine ("Tokens:");
			foreach (int token in _tokens) {
				Console.Out.WriteLine (_entries [token]);

				HashSet<int> activePlaceTransitions = new HashSet<int> (_reverseInputs [token]);

				activePlaceTransitions.IntersectWith (_order [i]);
				if (activePlaceTransitions.Count == 0)
					continue;

				activeTransitions.UnionWith (activePlaceTransitions);
				tokensToRemove.Add (token);
			}

			Console.Out.Write ("Active Transitions:");
			foreach (int tr in activeTransitions)
				Console.Out.Write (" " + tr.ToString ());
			Console.Out.WriteLine ();

			// Trigger active transitions.
			foreach (int tr in activeTransitions) {
				Dictionary<string, string> outputEntries = new Dictionary<string, string> ();
				string policy = _policies [tr];

				foreach (int j in _outputs[tr]) {
					string entryname = _entries [j];

					outputEntries.Add (entryname, HashEntry (entryname));
				}

				if (policy.Contains ("$(inputs)")) {
					List<string> inputEntries = new List<string> ();
					String inputs = "";
					HashSet<int> entriesToConsider = new HashSet<int> (_inputs [tr]);

					entriesToConsider.IntersectWith (_tokens);
					foreach (int entry in entriesToConsider)
						inputEntries.Add (_entries [entry]);

					for (int j = 0; j < inputEntries.Count; j++) {
						if (j != 0)
							inputs += " ";
						inputs += inputEntries [j];
					}

					policy = policy.Replace ("$(inputs)", inputs);
				}

				string[] program = policy.Split (new char[] { ' ', '\t' }, 2);

				Console.Out.WriteLine ("Execute: " + program [0] + " " + program [1]);

				using (Process proc = new System.Diagnostics.Process ()) {
					proc.EnableRaisingEvents = false;
					proc.StartInfo.FileName = program [0];
					proc.StartInfo.Arguments = program [1];
					proc.Start ();
					proc.WaitForExit ();
				}
				;

				List<Tuple<string,int>> outputEntriesToConsider =
					_cloudMakefile.FindMatchingEntries ("", new HashSet<int> (outputs [tr]));
			
				foreach (Tuple<string,int> entry in outputEntriesToConsider) {
					string entryname = entry.Item1;

					if ((outputEntries.ContainsKey (entryname)) &&
						(HashEntry (entryname) != outputEntries [entryname])) {
						tokensToAdd.Add (_entries.IndexOf (entryname));
					} else {
						tokensToAdd.Add (_entries.Count);
						AddEntry (entry);
					}
				}
			}

			_tokens.ExceptWith (tokensToRemove);
			_tokens.UnionWith (tokensToAdd);

			foreach (int token in tokensToAdd) {
				string entryname = _entries [token];
				string filename;

				filename = MatchTokenToConfigFile (entryname);
				if (filename != null) {
					configFiles.Add (filename);
					continue;
				}
				filename = MatchTokenToCloudMakeConfigFile (entryname);
				if (filename != null) {
					cloudMakeConfigFiles.Add (filename);
					continue;
				}
			}
		}

		_tokens.Clear ();
		_tokens.TrimExcess ();
		return new Tuple<HashSet<string>, HashSet<string>> (configFiles, cloudMakeConfigFiles);
	}
}

public enum Mode
{
	SIGNAL,
	PERIOD,
}

public enum UpdateStateType
{
	NEW_NODE,
	FAILED_NODE,
	STOPPED_PROC,
	UPDATE_STATE,
}

public class ProcessInfo
{
	private int _id;
	private string _name;
	private string _path;
	private string _exec;
	private string _args;
	private bool _debug;
	private string _debugFilename;

	public ProcessInfo (string name, string path, string exec, string args, bool debug, string debugFilename)
	{
		_name = name;
		_path = path;
		_exec = exec;
		_args = args;
		_debug = debug;
		_debugFilename = debugFilename;
	}

	public override string ToString ()
	{
		string res = "Proc: " + _name + Environment.NewLine;

		res += "Path: " + _path + Environment.NewLine;
		res += "Exec: " + _exec + Environment.NewLine;
		res += "Args: " + _args + Environment.NewLine;

		return res;
	}

	public int GetId ()
	{
		return _id;
	}

	public string GetName ()
	{
		return _name;
	}

	public string GetPath ()
	{
		return _path;
	}

	public string GetExec ()
	{
		return _exec;
	}

	public string GetArgs ()
	{
		return _args;
	}

	public void LaunchProcess ()
	{
		ProcessStartInfo startInfo = new ProcessStartInfo ();
		string oldPath = Directory.GetCurrentDirectory ();

		if (_path != "")
			Directory.SetCurrentDirectory (_path);

		startInfo.FileName = _exec;
		startInfo.Arguments = _args;

		if (_debug)
			CloudMake.WriteLine (_debugFilename, "Call " + startInfo.FileName + " " + startInfo.Arguments);
		using (Process proc = Process.Start (startInfo)) {
			_id = proc.Id;
		}
		;
		Directory.SetCurrentDirectory (oldPath);
	}
}

public class CloudMakeMonitor
{
	private CloudMake _cloudMake;

	public CloudMakeMonitor (CloudMake cloudMake)
	{
		_cloudMake = cloudMake;
	}

	public void LaunchMonitor (Isis.Group myGroup)
	{
		Dictionary<string,ProcessInfo> procs;

		while (true) {
			procs = new Dictionary<string, ProcessInfo> (_cloudMake.GetProcesses ());
			foreach (string procName in procs.Keys) {
				ProcessInfo procInfo = procs [procName];
				int procId = procInfo.GetId ();

				try {
					using (Process proc = Process.GetProcessById(procId)) {
						if ((proc == null) || proc.HasExited) {
							if (_cloudMake.GetDebug ())
								CloudMake.WriteLine (_cloudMake.GetMonitorDebugFilename (), "Process " + procName + " stopped.");
							_cloudMake.RemoveProcess (procName);
							// TODO: Send STOPPED_PROC message to the leader.
						}
					}
					;
				} catch (ArgumentException) {
					if (_cloudMake.GetDebug ())
						CloudMake.WriteLine (_cloudMake.GetMonitorDebugFilename (), "Process " + procName + " stopped.");
					_cloudMake.RemoveProcess (procName);
					// TODO: Send STOPPED_PROC message to the leader.
				}
			}
			System.Threading.Thread.Sleep (_cloudMake.GetTimeout ());
		}
	}
}

/**
 * Class that is responsible for printing the state of Isis.
 */
public class StatePrinter
{
	// In seconds.
	private int _printInterval;
	private int _nrState;

	public StatePrinter (int printInterval)
	{
		_printInterval = printInterval;
		_nrState = 0;
	}

	public void Run ()
	{
		while (true) {
			_nrState++;
			IsisSystem.WriteLine ("State " + _nrState.ToString () + " taken at " + DateTime.Now.ToString ());
			IsisSystem.WriteLine (IsisSystem.GetState ());
			Thread.Sleep (_printInterval * 1000);
		}
	}
}

public class RemoteProcess
{
	private string _nodename;
	private string _name;
	private string _path;
	private string _exec;
	private string _args;

	public RemoteProcess (string nodename, string name, string path, string exec, string args)
	{
		_nodename = nodename;
		_name = name;
		_path = path;
		_exec = exec;
		_args = args;
	}

	public override string ToString ()
	{
		string res = "Proc: " + _name + "@" + _nodename + Environment.NewLine;

		res += "Path: " + _path + Environment.NewLine;
		res += "Exec: " + _exec + Environment.NewLine;
		res += "Args: " + _args + Environment.NewLine;

		return res;
	}

	public string GetNodename ()
	{
		return _nodename;
	}

	public string GetName ()
	{
		return _name;
	}

	public string GetPath ()
	{
		return _path;
	}

	public string GetExec ()
	{
		return _exec;
	}

	public string GetArgs ()
	{
		return _args;
	}
}

/**
 * Class that constructs the CloudMake tool.
 */
public class CloudMake
{
	// Fields needed in local CloudMake node.
	private string _hostname;
	private string _groupname;
	private int _timeout;
	private Dictionary<string, ProcessInfo> _processes;
	private CloudMakeMonitor _cloudMakeMonitor;
	private Object _localLock;
	private bool _isLeader;
	private List<string> _leaderAddresses;
	private Object _leadersStructureLock;
	private CloudMakefile _cloudMakefile;
	private DependencyStructure _dependencyStructure;
	// Fields needed in the leader of CloudMake.
	private string _rootDirectory;
	private Dictionary<string, Address> _nameToAddress;
	private Dictionary<string, string> _addressToName;
	private Dictionary<Tuple<string, string>, Tuple<UpdateStateType,string>> _states;
	private Dictionary<Tuple<string, string>, string> _configs;
	private Object _leaderLock;
	// Debug information.
	public static bool _debug;
	private string _leaderDebugFilename;
	private string _localDebugFilename;
	private string _monitorDebugFilename;
	// Optimization flags.
	private bool _batching;
	private bool _parallel;
	// Message types for Isis.
	public static int ASK_INFO = 0;
	public static int REGISTER_LEADER = 1;
	public static int RUN_PROCESS = 2;
	public static int KILL_PROCESS = 3;
	public static int INIT_STATE_FILES = 4;
	public static int STOPPED_PROC = 5;
	public static int UPDATE_STATE = 6;
	public static int UPDATE_CONFIG = 7;
	public static int COMMIT_CONFIG = 8;

	public CloudMake (bool debug, string hostname, string groupname, bool isLeader, bool parallel, bool batching,
	                  int timeout)
	{
		string debugDir = Directory.GetCurrentDirectory () + Path.DirectorySeparatorChar + "Debug";

		_debug = debug;
		_hostname = hostname;
		_groupname = groupname;
		_isLeader = isLeader;
		_batching = batching;
		_parallel = parallel;
		_timeout = timeout;
		_localDebugFilename = debugDir + Path.DirectorySeparatorChar + "CloudMakeLocal.txt";
		_monitorDebugFilename = debugDir + Path.DirectorySeparatorChar + "CloudMakeMonitor.txt";
		_processes = new Dictionary<string, ProcessInfo> ();
		_localLock = new Object ();
		_cloudMakeMonitor = new CloudMakeMonitor (this);
		_leaderAddresses = new List<String> ();
		_leadersStructureLock = new Object ();
		_cloudMakefile = new CloudMakefile ();
		_dependencyStructure = new DependencyStructure (_cloudMakefile);
		if (_isLeader) {
			_leaderDebugFilename = debugDir + Path.DirectorySeparatorChar + "CloudMakeLeader.txt";
			_rootDirectory = "DFiles";
			_nameToAddress = new Dictionary<string, Address> ();
			_addressToName = new Dictionary<string, string> ();
			_states = new Dictionary<Tuple<string, string>, Tuple<UpdateStateType, string>> ();
			_configs = new Dictionary<Tuple<string, string>, string> ();
			_leaderLock = new Object ();
		}
	}

	static public string GetMyIpAddress (string debugFilename)
	{
		IPHostEntry host;
		string localIP = "?";
		bool localIPFound = false;
		bool networkInterfaceOk = false;

		while (!localIPFound) {
			try {
				host = Dns.GetHostEntry (Dns.GetHostName ());
				foreach (IPAddress ip in host.AddressList) {
					WriteLine (debugFilename, "Trying " + ip.ToString ());
					if (ip.AddressFamily == AddressFamily.InterNetwork) {
						localIP = ip.ToString ();
						localIPFound = true;
						break;
					}
				}
				if (!localIPFound)
					Thread.Sleep (1000);
			} catch (Exception) {
				WriteLine (debugFilename, "Unable to resolve my IP Address. Trying again ...");
				Thread.Sleep (1000);
			}
		}
		WriteLine (debugFilename, "");

		while (!networkInterfaceOk) {
			WriteLine (debugFilename, "Trying Network Interfaces");
			foreach (NetworkInterface netInterface in NetworkInterface.GetAllNetworkInterfaces()) {
				WriteLine (debugFilename, "Name: " + netInterface.Name);
				WriteLine (debugFilename, "Description: " + netInterface.Description);
				WriteLine (debugFilename, "Addresses: ");
				IPInterfaceProperties ipProps = netInterface.GetIPProperties ();
				foreach (UnicastIPAddressInformation addr in ipProps.UnicastAddresses) {
					WriteLine (debugFilename, " " + addr.Address.ToString ());
					if (localIP.Equals (addr.Address.ToString ())) {
						networkInterfaceOk = true;
						break;
					}
				}
				if (networkInterfaceOk)
					break;
				WriteLine (debugFilename, "");
			}
			if (!networkInterfaceOk) {
				WriteLine (debugFilename, "Unable to obtain the correct network interface. Trying again ...");
				Thread.Sleep (1000);
			}
		}
		WriteLine (debugFilename, "");

		return localIP;
	}

	static public string GetIpAddress (string hostname, string debugFilename)
	{
		IPHostEntry host;
		string IP = "?";
		bool IPFound = false;

		while (!IPFound) {
			try {
				host = Dns.GetHostEntry (hostname);
				foreach (IPAddress ip in host.AddressList) {
					WriteLine (debugFilename, "Trying " + ip.ToString ());
					if (ip.AddressFamily == AddressFamily.InterNetwork) {
						IP = ip.ToString ();
						IPFound = true;
					}
				}
				if (!IPFound)
					Thread.Sleep (1000);
			} catch (Exception) {
				WriteLine (debugFilename, "Unable to resolve IP Address. Trying again ...");
				Thread.Sleep (1000);
			}
		}
		WriteLine (debugFilename, "");

		return IP;
	}

	public int GetTimeout ()
	{
		return _timeout;
	}

	public string GetHostName ()
	{
		return _hostname; 
	}

	public Dictionary<string, ProcessInfo> GetProcesses ()
	{
		Dictionary<string, ProcessInfo> res;

		lock (_localLock) {
			res = new Dictionary<string, ProcessInfo> (_processes);
		}

		return res;
	}

	public void RemoveProcess (string procName)
	{
		lock (_localLock) {
			if (_processes.ContainsKey (procName))
				_processes.Remove (procName);
		}
	}

	public bool GetDebug ()
	{
		return _debug;
	}

	public string GetMonitorDebugFilename ()
	{
		return _monitorDebugFilename;
	}

	public void LaunchMonitor (Isis.Group myGroup)
	{
		_cloudMakeMonitor.LaunchMonitor (myGroup);
	}

	private Tuple<HashSet<string>, List<RemoteProcess>> ProcessCloudMakeConfigFiles (Isis.Group myGroup,
	                                                                                 HashSet<string> cloudMakeConfigFiles)
	{
		HashSet<string> configFiles = new HashSet<string> ();
		List<RemoteProcess> remoteProcesses = new List<RemoteProcess> ();

		foreach (string cloudMakeConfigFile in cloudMakeConfigFiles) {
			XmlDocument xmlDoc = new XmlDocument ();
			XmlNode xmlNode;

			xmlDoc.Load (cloudMakeConfigFile);
			xmlNode = xmlDoc.DocumentElement;
			string nodename = cloudMakeConfigFile.Substring (0, cloudMakeConfigFile.IndexOf ('/'));
			foreach (XmlNode childNode in xmlNode.ChildNodes) {
				switch (childNode.Name) {
				case ("process"):
					if (childNode.Attributes ["action"].Value == "Run") {
						string name = childNode ["name"].InnerText;
						string path = childNode ["path"].InnerText;
						string exec = childNode ["exec"].InnerText;
						string args = childNode ["args"].InnerText;

						if (_debug)
							CloudMake.WriteLine (_leaderDebugFilename, "Run process " + name + ".");
						remoteProcesses.Add (new RemoteProcess (nodename, name, path, exec, args));
					} else if (childNode.Attributes ["action"].Value == "stop") {
						throw new Exception ("Not implemented yet.");
					}
					break;
				case ("stateFile"):
					throw new Exception ("Not implemented yet.");
				case ("configFile"):
					string configPath = childNode ["path"].InnerText;
					string configName = childNode ["name"].InnerText;
					string configFilename = (configPath == "") ? nodename + "/" + configName : nodename + "/" +
						configPath + "/" + configName;

					_dependencyStructure.AddConfigFile (configFilename);
					configFiles.Add (configFilename);
					break;
				default:
					if (_debug)
						CloudMake.WriteLine (_leaderDebugFilename, "Operation " + childNode.Name + " is not supported " +
							"from CloudMake.");
					break;
				}
			}
		}

		return new Tuple<HashSet<string>, List<RemoteProcess>> (configFiles, remoteProcesses);
	}

	private void Dist (Isis.Group myGroup, Tuple<HashSet<string>,HashSet<string>> activeFiles)
	{
		HashSet<string> activeConfigFiles = activeFiles.Item1;
		HashSet<string> activeCloudMakeConfigFiles = activeFiles.Item2;
		Tuple<HashSet<string>, List<RemoteProcess>> processedInfo =
			ProcessCloudMakeConfigFiles (myGroup, activeCloudMakeConfigFiles);
		HashSet<string> additionalActiveConfigFiles = processedInfo.Item1;
		List<RemoteProcess> remoteProcesses = processedInfo.Item2;

		activeConfigFiles.UnionWith (additionalActiveConfigFiles);
		if (_debug) {
			CloudMake.WriteLine (_leaderDebugFilename, "Files to Distribute:");
			foreach (string filename in activeConfigFiles)
				CloudMake.WriteLine (_leaderDebugFilename, filename);
		}

		foreach (string activeConfigFile in activeConfigFiles) {
			List<string> fields = new List<string> (activeConfigFile.Split ('/'));
			string nodename = fields [0];
			List<string> path = fields.GetRange (1, fields.Count - 2);
			string name = fields [fields.Count - 1];
			string filename = nodename;
			XmlDocument xmlDoc = new XmlDocument ();
			string content;

			foreach (string tempPath in path)
				filename += Path.DirectorySeparatorChar.ToString () + tempPath;
			filename += Path.DirectorySeparatorChar.ToString () + name;
			xmlDoc.Load (filename);
			content = xmlDoc.OuterXml;
			WriteLine (_leaderDebugFilename, "Distribute the content of " + filename + " to " + nodename);
			myGroup.P2PSend (_nameToAddress [nodename], UPDATE_CONFIG, path, name, content);
		}

		foreach (RemoteProcess proc in remoteProcesses)
			myGroup.P2PSend (_nameToAddress [proc.GetNodename ()], RUN_PROCESS, proc.GetName (), proc.GetPath (),
			                 proc.GetExec (), proc.GetArgs ());
		/*
		string cloudMakeDirectory, nodeDirectory, procDirectory, xmlAppConfigFile, node, proc, content;
		string[] nodes;
		List<string> nodeApps, msgApps, msgs;
		XmlDocument xmlDoc = new XmlDocument ();
		XmlNode xmlNode;
		int myRank = myGroup.GetMyRank ();

		nodes = Directory.GetDirectories (_rootDirectory);
		foreach (string temp in nodes) {
			msgApps = new List<string> ();
			msgs = new List<string> ();
			node = temp.Remove (0, temp.IndexOf (Path.DirectorySeparatorChar) + 1);
			nodeDirectory = _rootDirectory + Path.DirectorySeparatorChar + node;
			cloudMakeDirectory = nodeDirectory + Path.DirectorySeparatorChar + "CloudMake";
			xmlDoc.Load (cloudMakeDirectory + Path.DirectorySeparatorChar + "config.xml");
			content = xmlDoc.OuterXml;
			if (!content.Equals (_configs [new Tuple<string, string> (node, "CloudMake")])) {
				xmlNode = xmlDoc.DocumentElement;
				nodeApps = _procs [node];
				foreach (XmlNode procNode in xmlNode.ChildNodes) {
					proc = procNode.LocalName;
					if (!nodeApps.Contains (proc)) {
						procDirectory = nodeDirectory + Path.DirectorySeparatorChar + proc;
						Directory.CreateDirectory (procDirectory);
						xmlAppConfigFile = procDirectory + Path.DirectorySeparatorChar + "config.xml";
						using (XmlWriter writer = XmlWriter.Create(xmlAppConfigFile)) {
							writer.WriteStartDocument ();
							writer.WriteComment ("XML document corresponding to the process configuration.");
							writer.WriteStartElement ("Config");
							writer.WriteEndElement ();
							writer.WriteEndDocument ();
						}
						nodeApps.Add (proc);
						_configs.Add (new Tuple<string, string> (node, proc), File.ReadAllText (xmlAppConfigFile));
					}
				}
				_procs [node] = nodeApps;
				msgApps.Add ("CloudMake");
				msgs.Add (content);
				_configs [new Tuple<string, string> (node, "CloudMake")] = content;
				if (!_batching) {
					if (_debug)
						WriteLine (node + " " + _nameToAddress [node].ToStringVerboseFormat () + " - " +
							msgApps [0] + ": " + msgs [0]);
					myGroup.P2PSend (_nameToAddress [node], UPDATE_CONFIG, myRank, logicalClock, msgApps, msgs);
					msgApps.Clear ();
					msgs.Clear ();
				}
			}
			foreach (string procID in _procs[node]) {
				if (procID == "CloudMake")
					continue;
				procDirectory = nodeDirectory + Path.DirectorySeparatorChar + procID;
				xmlDoc.Load (procDirectory + Path.DirectorySeparatorChar + "config.xml");
				content = xmlDoc.OuterXml;
				if (!content.Equals (_configs [new Tuple<string, string> (node, procID)])) {
					msgApps.Add (procID);
					msgs.Add (content);
					_configs [new Tuple<string, string> (node, procID)] = content;
					if (!_batching) {
						if (_debug)
							WriteLine (node + " " + _nameToAddress [node].ToStringVerboseFormat () + " - " +
								msgApps [0] + ": " + msgs [0]);
						myGroup.P2PSend (_nameToAddress [node], UPDATE_CONFIG, myRank, logicalClock, msgApps, msgs);
						msgApps.Clear ();
						msgs.Clear ();
					}
				}
			}
			if (_batching && msgApps.Count != 0) {
				if (_debug)
					for (int i = 0; i < msgApps.Count; i++)
						WriteLine (node + " " + _nameToAddress [node].ToStringVerboseFormat () + " - " + msgApps [i] +
							": " + msgs [i]);
				myGroup.P2PSend (_nameToAddress [node], UPDATE_CONFIG, myRank, logicalClock, msgApps, msgs);
				msgApps.Clear ();
				msgs.Clear ();
			}
			if (_isReplicated) {
				myGroup.OrderedSend (COMMIT_CONFIG, myRank, logicalClock);
			}
		}*/
	}

	public Address GetLeader (Isis.Group myGroup)
	{
		View myView = myGroup.GetView ();

		return myView.members [0];
	}
	/*
	public List<Address> GetLeaderList (Isis.Group myGroup)
	{
		View myView = myGroup.GetView ();
		List<Address> leaderList = new List<Address> ();
		int cloudMakeLeaderSize = Math.Min (NR_LEADER_REPLICAS, myView.GetSize ());

		for (int i = 0; i < cloudMakeLeaderSize; i++)
			leaderList.Add (myView.members [i]);
		return leaderList;
	}
	*/
	private void UpdateGlobalState (Dictionary<Tuple<string, string>, Tuple<UpdateStateType, string>> states)
	{
		UpdateStateType type;
		string nodename, procId, msg;

		foreach (Tuple<string,string> key  in states.Keys) {
			nodename = key.Item1;
			procId = key.Item2;
			type = states [key].Item1;
			msg = states [key].Item2;
			switch (type) {
			case (UpdateStateType.NEW_NODE):
				NewNode (nodename);
				break;
			case (UpdateStateType.FAILED_NODE):
				// filename = nodename + Path.DirectorySeparatorChar.ToString () + "failed_node";
				// _cloudMakefile.Mark (filename);
				break;
			case (UpdateStateType.STOPPED_PROC):
				// filename = nodename + Path.DirectorySeparatorChar.ToString () + procId +
				// 	Path.DirectorySeparatorChar.ToString () + "stopped_proc";
				// _cloudMakefile.Mark (filename);
				break;
			case (UpdateStateType.UPDATE_STATE):
				UpdateState (nodename, procId, msg);
				break;
			}
		}
	}

	public static string GetXMLAsString (string file)
	{
		XmlDocument doc = new XmlDocument ();

		try {
			doc.Load (file);
		} catch (XmlException) {
			return null;
		}
		return doc.OuterXml;
	}

	public static string GetHash (string text)
	{
		byte[] bytes = GetBytes (text);
		SHA256Managed hashstring = new SHA256Managed ();
		byte[] hash = hashstring.ComputeHash (bytes);

		return GetString (hash);
	}

	private static byte[] GetBytes (string str)
	{
		byte[] bytes = new byte[str.Length * sizeof(char)];
		System.Buffer.BlockCopy (str.ToCharArray (), 0, bytes, 0, bytes.Length);
		return bytes;
	}

	private static string GetString (byte[] bytes)
	{
		char[] chars = new char[bytes.Length / sizeof(char)];
		System.Buffer.BlockCopy (bytes, 0, chars, 0, bytes.Length);
		return new string (chars);
	}

	private void FindInitialEntries ()
	{
		List<Tuple<string,int>> entries = _cloudMakefile.FindMatchingEntries ("");

		foreach (Tuple<string, int> entry in entries) {
			Console.Out.WriteLine (entry.Item1 + ":" + entry.Item2.ToString ());
			_dependencyStructure.AddEntry (entry);
			_dependencyStructure.AddTokenToEntry (entry.Item1);
		}
	}

	private int Rank (string hostname)
	{
		if (_leaderAddresses.Contains (hostname))
			return _leaderAddresses.IndexOf (hostname);
		return -1;
	}

	private int MyRank ()
	{
		return Rank (_hostname);
	}

	private static void EnsureDir (List<string> path)
	{
		if (path.Count == 0)
			return;

		string dirname = path [0];
		int cur = 1;

		while ((cur < path.Count) && (Directory.Exists (dirname))) {
			dirname += Path.DirectorySeparatorChar.ToString () + path [cur];
			cur++;
		}

		while (cur < path.Count) {
			Directory.CreateDirectory (dirname);
			dirname += Path.DirectorySeparatorChar.ToString () + path [cur];
			cur++;
		}
	}

	private void LaunchCloudMakeLeader (Isis.Group myGroup)
	{
		Dictionary<Tuple<string, string>, Tuple<UpdateStateType, string>> states;
		Tuple<HashSet<string>, HashSet<string>> activeFiles;

		myGroup.SafeSend (REGISTER_LEADER, _hostname);
		WriteLine (_leaderDebugFilename, "Registered with rank " + MyRank ().ToString ());

		lock (_leadersStructureLock) {
			while (MyRank() != 0)
				Monitor.Wait (_leadersStructureLock);
		}

		if (!Directory.Exists (_rootDirectory))
			Directory.CreateDirectory (_rootDirectory);
		Directory.SetCurrentDirectory (_rootDirectory);
		FindInitialEntries ();

		while (true) {
			lock (_leaderLock) {
				while (_states.Count == 0)
					Monitor.Wait (_leaderLock);
				states = new Dictionary<Tuple<string, string>, Tuple<UpdateStateType, string>> (_states);
				_states.Clear ();
			}
			UpdateGlobalState (states);
			WriteLine (_leaderDebugFilename, _dependencyStructure.ToString ());
			activeFiles = _dependencyStructure.Run ();
			Dist (myGroup, activeFiles);
		}
	}

	private void NewNode (string nodename)
	{
		string nodeDirectory = nodename;
		string filename = nodename + Path.DirectorySeparatorChar.ToString () + "new_node";
		string cloudMakeDirectory = nodeDirectory + Path.DirectorySeparatorChar + "CloudMake";
		string cloudMakeConfigFilename = cloudMakeDirectory + Path.DirectorySeparatorChar.ToString () + "config.xml";
		List<Tuple<string,int>> entries = _cloudMakefile.FindMatchingEntries (filename);

		Directory.CreateDirectory (nodeDirectory);
		Directory.CreateDirectory (cloudMakeDirectory);

		if (entries.Count > 1)
			throw new Exception ("It is impossible for a NEW_NODE event to have more than one corresponding DFA.");
		if (entries.Count == 1) {
			_dependencyStructure.AddEntry (entries [0]);
			_dependencyStructure.AddTokenToEntry (entries [0].Item1);
		}

		_dependencyStructure.AddCloudMakeConfigFile (cloudMakeConfigFilename);
		if (_debug)
			WriteLine (_leaderDebugFilename, "New Node Event: " + filename);
	}

	private void FailedNodes (Isis.Group myGroup, Address[] nodes)
	{
		foreach (Address node in nodes) {
			string nodename = _addressToName [node.ToStringVerboseFormat ()];
			string xmlFilename = nodename + Path.DirectorySeparatorChar + "CloudMake" + Path.DirectorySeparatorChar +
				"NodeStatus.xml";

			WriteLine (_leaderDebugFilename, "Failed Node: " + nodename);
			lock (_leaderLock) {
				XmlDocument doc = new XmlDocument ();
				XmlNode xmlNode;

				doc.Load (xmlFilename);
				xmlNode = doc.DocumentElement;
				foreach (XmlNode xn in xmlNode.ChildNodes)
					if (xn.Name == "Status")
						xn.InnerText = "failed";
				doc.Save (xmlFilename);
			}
		}
		WriteLine (_leaderDebugFilename, "Call CloudMake from Failed Nodes.");

		lock (_leaderLock) {
			foreach (Address node in nodes) {
				string nodename = _addressToName [node.ToStringVerboseFormat ()];

				if (_nameToAddress.ContainsKey (nodename))
					_nameToAddress.Remove (nodename);
				if (_addressToName.ContainsKey (node.ToStringVerboseFormat ()))
					_addressToName.Remove (node.ToStringVerboseFormat ());
			}
		}
	}

	private void StoppedProc (Isis.Group myGroup, Address node, List<string> procIds)
	{
		string nodename = _addressToName [node.ToStringVerboseFormat ()];
		string nodeDirectory = nodename;
		List<string> procDirectories = new List<string> ();
		string failedAppFilename;
			
		lock (_leaderLock) {
			for (int i = 0; i < procIds.Count; i++) {
				WriteLine (_leaderDebugFilename, "Application failed: " + nodename + "-" + procIds [i]);
				procDirectories.Add (nodeDirectory + Path.DirectorySeparatorChar + procIds [i]);
				failedAppFilename = procDirectories [i] + Path.DirectorySeparatorChar + "failed_proc";
				File.Create (failedAppFilename);
				// _procs [nodename].Remove (procIds [i]);
				_configs.Remove (new Tuple<string,string> (nodename, procIds [i]));
			}
		}
		lock (_leaderLock) {
			WriteLine (_leaderDebugFilename, "Call CloudMake from Failed Apps.");
			foreach (string d in procDirectories) {
				File.Delete (d + Path.DirectorySeparatorChar + "failed_proc");
			}
		}
	}

	private void UpdateState (string nodename, string procId, string msg)
	{
		string stateFilename = nodename + Path.DirectorySeparatorChar + procId + Path.DirectorySeparatorChar +
			"state.xml";
		XmlDocument xmlDoc = new XmlDocument ();

		xmlDoc.LoadXml (msg);
		xmlDoc.Save (stateFilename);
	}
	/*
	private void UpdateConfig (Isis.Group myGroup, long logicalClock, List<string> procs, List<string> configs)
	{
		int i;
		string proc, path, execute, stateFilename, configFilename;
		ProcessInfo procInfo;
		XmlDocument xmlDoc = new XmlDocument ();
		XmlNode xmlNode, cloudMakeNode, configNode;

		if (procs.Contains ("CloudMake")) {
			i = procs.IndexOf ("CloudMake");
			xmlDoc.LoadXml (configs [i]);
			xmlNode = xmlDoc.DocumentElement;
			lock (_localLock) {
				if (_localLogicalClock ["CloudMake"] < logicalClock) {
					foreach (XmlNode procXmlNode in xmlNode.ChildNodes) {
						proc = procXmlNode.LocalName;
						if (!_processes.ContainsKey (proc)) {
							cloudMakeNode = procXmlNode ["CloudMake"];
							configNode = procXmlNode ["Config"];
							path = cloudMakeNode ["Path"].InnerText;
							execute = cloudMakeNode ["Execute"].InnerText;
							stateFilename = cloudMakeNode ["StateFilename"].InnerText;
							configFilename = cloudMakeNode ["ConfigFilename"].InnerText;
							procInfo = new ProcessInfo (myGroup, this, proc, path, execute, stateFilename,
							                            configFilename, configNode.OuterXml);
							procInfo.LaunchProcess ();
							_processes.Add (proc, procInfo);
							_localLogicalClock.Add (proc, 0);
							WriteLine ("New Application: " + procInfo.GetId ());
							WriteLine ("Executable: " + procInfo.GetExecute ());
						} else {
							proc = procXmlNode.LocalName;
							procInfo = _processes [proc];
						}
					}
				}
			}
			procs.RemoveAt (i);
			configs.RemoveAt (i);
		}

		lock (_localLock) {
			for (i = 0; i < procs.Count; i++) {
				if (!_processes.ContainsKey (procs [i])) {
					WriteLine ("There is no application process with name " + procs [i]);
					continue;
				}
				if (_localLogicalClock [procs [i]] < logicalClock) {
					_localLogicalClock [procs [i]] = logicalClock;
					procInfo = _processes [procs [i]];
					path = procInfo.GetPath ();
					configFilename = procInfo.GetConfigFile ();
					xmlDoc.LoadXml (configs [i]);
					xmlDoc.Save (configFilename);
					long time = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
					using (StreamWriter sw = File.AppendText("receive.txt")) {
						sw.WriteLine (_localLogicalClock [procs [i]] + " " + time.ToString ());
					}
				}
			}
		}
	}
	*/
	private void addHandlers (Isis.Group myGroup)
	{
		myGroup.ViewHandlers += (ViewHandler)delegate(View v) {
			List<string> nameList = new List<string> ();
			List<Address> addrList = new List<Address> ();
			List<bool> isLeaderList = new List<bool> ();
			Tuple<string,string> key;
			Tuple<UpdateStateType,string> value;
			EOLMarker myEOL = new EOLMarker ();
			List<Address> joiners = new List<Address> ();
			List<Address> leavers = new List<Address> ();

			// Add new nodes to all the necessary structures.
			joiners.AddRange (v.joiners);
			if (joiners.Count > 0) {
				if (_isLeader) {
					myGroup.Query (joiners.Count, joiners, ASK_INFO, myEOL, nameList, addrList, isLeaderList);
					lock (_leaderLock) {
						for (int i = 0; i < joiners.Count; i++) {
							string name = nameList [i];
							Address addr = addrList [i];
							bool isLeader = isLeaderList [i];

							CloudMake.WriteLine (_leaderDebugFilename, "New node entered the group " + _groupname);
							CloudMake.WriteLine (_leaderDebugFilename, "Name: " + name);
							CloudMake.WriteLine (_leaderDebugFilename, "Address: " + addr.ToStringVerboseFormat ());
							CloudMake.WriteLine (_leaderDebugFilename, "IsLeader: " + isLeader.ToString ());

							_nameToAddress [name] = addr;
							_addressToName [addr.ToStringVerboseFormat ()] = name;
							key = new Tuple<string, string> (name, "CloudMake");
							value = new Tuple<UpdateStateType, string> (UpdateStateType.NEW_NODE, null);
							_states [key] = value;
						}
						Monitor.PulseAll (_leaderLock);
					}
				}
			}
			// Remove failed nodes from all the necessary structures.
			leavers.AddRange (v.leavers);
			if (leavers.Count > 0) {
				if (_isLeader) {
					lock (_leaderLock) {
						for (int i = 0; i < leavers.Count; i++) {
							Address addr = leavers [i];
							string name = _addressToName [addr.ToStringVerboseFormat ()];
							bool isLeader = _leaderAddresses.Contains (name);

							CloudMake.WriteLine (_leaderDebugFilename, "New node entered the group " + _groupname);
							CloudMake.WriteLine (_leaderDebugFilename, "Name: " + name);
							CloudMake.WriteLine (_leaderDebugFilename, "Address: " + addr.ToStringVerboseFormat ());
							CloudMake.WriteLine (_leaderDebugFilename, "IsLeader: " + isLeader.ToString ());

							_nameToAddress.Remove (name);
							_addressToName.Remove (addr.ToStringVerboseFormat ());
							key = new Tuple<string, string> (name, "CloudMake");
							value = new Tuple<UpdateStateType, string> (UpdateStateType.FAILED_NODE, null);
							_states [key] = value;
						}
						Monitor.PulseAll (_leaderLock);
					}
				}
				lock (_leadersStructureLock) {
					for (int i = 0; i < leavers.Count; i++) {
						Address addr = leavers [i];
						string name = _addressToName [addr.ToStringVerboseFormat ()];
						bool isLeader = _leaderAddresses.Contains (name);

						if (isLeader) {
							_leaderAddresses.Remove (name);
							Monitor.PulseAll (_leadersStructureLock);
						}
					}
				}
			}
		};

		myGroup.Handlers [REGISTER_LEADER] += (registerLeaderArgs)delegate(string nodename) {
			lock (_leadersStructureLock) {
				_leaderAddresses.Add (nodename);
				CloudMake.WriteLine (_localDebugFilename, "Leader " + nodename + " is registered with rank " +
					Rank (nodename).ToString () + ".");
				Monitor.PulseAll (_leadersStructureLock);
			}
		};

		myGroup.Handlers [RUN_PROCESS] += (runProcessArgs)delegate(string procName, string path, string exec,
		                                                           string args) {
			lock (_localLock) {
				if (_processes.ContainsKey (procName) && _debug) {
					CloudMake.WriteLine (_localDebugFilename, "Process " + procName + " already exists.");
					return;
				}

				ProcessInfo procInfo = new ProcessInfo (procName, path, exec, args, _debug, _localDebugFilename);
				
				if (_debug) {
					CloudMake.WriteLine (_localDebugFilename, "Run Process " + procName + ".");
					CloudMake.WriteLine (_localDebugFilename, procInfo.ToString ());
				}
				procInfo.LaunchProcess ();
				_processes.Add (procName, procInfo);
			}
		};

		myGroup.Handlers [KILL_PROCESS] += (killProcessArgs)delegate(string procName) {
			lock (_localLock) {
				if (!_processes.ContainsKey (procName) && _debug) {
					if (_debug)
						CloudMake.WriteLine (_localDebugFilename, "Process " + procName + " does not exist.");
					return;
				}

				try {
					using (Process proc = Process.GetProcessById(_processes[procName].GetId())) {
						proc.CloseMainWindow ();
						proc.Close ();
						if (_debug)
							CloudMake.WriteLine (_localDebugFilename, "Kill Process " + procName + ".");
					}
					;
				} catch (Exception) {
				}
			}
		};

		myGroup.Handlers [ASK_INFO] += (askInfoArgs)delegate() {
			CloudMake.WriteLine (_localDebugFilename, "Leader asked for my info.");
			myGroup.Reply (GetHostName (), IsisSystem.GetMyAddress (), _isLeader);
		};

		myGroup.Handlers [STOPPED_PROC] += (stoppedProcArgs)delegate(string nodename, List<string> procs) {
			Tuple<string,string> key;
			Tuple<UpdateStateType,string> value;

			lock (_leaderLock) {
				for (int i = 0; i < procs.Count; i++) {
					WriteLine (_leaderDebugFilename, "Stopped Application: " + nodename + " " + procs [i]);
					// _procs [nodename].Remove (procs [i]);
					key = new Tuple<string, string> (nodename, procs [i]);
					value = new Tuple<UpdateStateType, string> (UpdateStateType.STOPPED_PROC, null);
					_states [key] = value;
					_configs.Remove (key);
					// _logicalClock += 1;
				}
				Monitor.PulseAll (_leaderLock);
			}
		};

		myGroup.Handlers [UPDATE_STATE] += (updateStateArgs)delegate(string nodename,List<string> procs,
		                                                             List<string> msgs) {
			Tuple<string,string> key;
			Tuple<UpdateStateType,string> value;

			lock (_leaderLock) {
				for (int i = 0; i < procs.Count; i++) {
					WriteLine (_leaderDebugFilename, "Update State: " + nodename + " " + procs [i]);
					key = new Tuple<string, string> (nodename, procs [i]);
					value = new Tuple<UpdateStateType, string> (UpdateStateType.UPDATE_STATE, msgs [i]);
					_states [key] = value;
					// _logicalClock += 1;
				}
				Monitor.PulseAll (_leaderLock);
			}
		};

		myGroup.Handlers [UPDATE_CONFIG] += (updateConfigArgs)delegate(List<string> path, string name, string content) {
			string filename;
			XmlDocument xmlDoc = new XmlDocument ();

			EnsureDir (path);
			if (path.Count != 0) {
				filename = path [0];
				for (int i = 1; i < path.Count; i++)
					filename += Path.DirectorySeparatorChar.ToString () + path [i];
				filename += Path.DirectorySeparatorChar.ToString () + name;
			} else {
				filename = name;
			}

			xmlDoc.LoadXml (content);
			xmlDoc.Save (filename);

			if (_debug) {
				WriteLine (_localDebugFilename, "Leader asked to update configuration.");
				WriteLine (_localDebugFilename, filename + ": " + content);
			}
		};
	}

	private Isis.Group beMaster (string groupname, int nworkers)
	{
		// Prepare Master.
		bool done = false;
		Address[] myWorkers = new Address[nworkers];
		Address[] addWorker = new Address[1];
		int count = 0;
		Isis.Group myGroup;
		Thread monitorThread;
		// Thread statePrinterThread;

		// Start the Isis Group.
		WriteLine (_localDebugFilename, "Start as a master.");
		IsisSystem.Start (true);
		myGroup = new Isis.Group (groupname);

		// Start printing the state of the system.
		/*
		WriteLine ("Start printer of Isis state.");
		statePrinterThread = new Thread (delegate() {
			StatePrinter statePrinter = new StatePrinter (10);
			statePrinter.Run ();
		}
		);
		statePrinterThread.Start ();*/

		// Add handlers to the worker.
		WriteLine (_localDebugFilename, "Add handlers.");
		addHandlers (myGroup);

		// Get Master Address.
		string myAddr = IsisSystem.GetMyAddress ().ToStringVerboseFormat ();
		/*using (System.IO.StreamWriter file = new System.IO.StreamWriter("masterAddress.txt")) {
			file.WriteLine (myAddr);
		}*/
		if (_debug)
			WriteLine (_localDebugFilename, "My Isis Address is " + myAddr);

		// Master function.
		IsisSystem.RegisterAsMaster ((NewWorker)delegate(Address hisAddress) {
			lock (myWorkers) {
				if (done) {
					addWorker [0] = hisAddress;
					/*
					Console.Out.WriteLine ("Batch Start {0}", hisAddress);
					IsisSystem.BatchStart (addWorker);
					Console.Out.WriteLine ("Wait For Worker Setup {0}", hisAddress);
					IsisSystem.WaitForWorkerSetup (addWorker);
					Console.Out.WriteLine ("Multi Join {0}", hisAddress);
					Isis.Group.multiJoin(addWorker, new Isis.Group[] { myGroup });
					*/
				} else {
					WriteLine (_localDebugFilename,
					           "Worker's Isis Address is " + hisAddress.ToStringVerboseFormat () + ".");
					myWorkers [count] = hisAddress;
					count++;
				}
			}
		}
		);

		// Wait until you collect all workers.
		while (true) {
			lock (myWorkers) {
				WriteLine (_localDebugFilename, "Count: " + count);
				if (count == nworkers) {
					done = true;
					break;
				}
			}
			Thread.Sleep (2000);
		}

		// Create the group.
		WriteLine (_localDebugFilename, "Create the group.");
		myGroup.Join ();

		// Now we activate all the Workers simultaneously.
		WriteLine (_localDebugFilename, "Start the workers.");
		IsisSystem.BatchStart (myWorkers);

		// This delays until they have all finished their batch start.
		WriteLine (_localDebugFilename, "Wait until all workers are finished.");
		IsisSystem.WaitForWorkerSetup (myWorkers);

		// MultiJoin all nodes.
		WriteLine (_localDebugFilename, "Multijoin.");
		Isis.Group.multiJoin (myWorkers, new Isis.Group[] { myGroup });
		/*long time = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
		using (StreamWriter sw = File.AppendText("start.txt")) {
			sw.WriteLine (time.ToString ());
		}*/

		// Initiate CloudMakeMonitor Thread on the local node.
		WriteLine (_localDebugFilename, "Add monitor thread.");
		monitorThread = new Thread (delegate() {
			LaunchMonitor (myGroup);
		}
		);
		monitorThread.Start ();

		return myGroup;
	}

	private Isis.Group beWorker (string groupname, string masterIp)
	{
		Isis.Group myGroup;
		Thread monitorThread;
		// Thread statePrinterThread;
		// Random rnd = new Random ();
		// Thread.Sleep ((rnd.Next (0, 10)) * 10000);

		// Form the master Isis Address from a file.
		WriteLine (_localDebugFilename, "Run as a worker and serve master " + masterIp + ".");
		IsisSystem.RunAsWorker (masterIp, 1800000);

		// Start printing the state of the system.
		/*
		Console.Out.WriteLine ("Start printer of Isis state.");
		statePrinterThread = new Thread (delegate() {
			StatePrinter statePrinter = new StatePrinter (10);
			statePrinter.Run ();
		}
		);
		statePrinterThread.Start ();*/

		// Start the Isis Group.
		WriteLine (_localDebugFilename, "Start as a worker.");
		IsisSystem.Start (false);
		myGroup = new Isis.Group (groupname);

		// Add handlers to the worker.
		WriteLine (_localDebugFilename, "Add handlers.");
		addHandlers (myGroup);

		// Now we activate all the Workers simultaneously.
		WriteLine (_localDebugFilename, "Setup Done.");
		IsisSystem.WorkerSetupDone ();

		// Initiate CloudMakeMonitor Thread on the local node.
		WriteLine (_localDebugFilename, "Add monitor thread.");
		monitorThread = new Thread (delegate() {
			LaunchMonitor (myGroup);
		}
		);
		monitorThread.Start ();

		return myGroup;
	}

	private Isis.Group beMember (string groupname)
	{
		Isis.Group myGroup;
		Thread monitorThread;
		// Thread statePrinterThread;

		// Form the master Isis Address from a file.
		WriteLine (_localDebugFilename, "Run as group member.");

		// Start printing the state of the system.
		/*
		Console.Out.WriteLine ("Start printer of Isis state.");
		statePrinterThread = new Thread (delegate() {
			StatePrinter statePrinter = new StatePrinter (10);
			statePrinter.Run ();
		}
		);
		statePrinterThread.Start ();
		*/

		// Start the Isis Group.
		WriteLine (_localDebugFilename, "Start as a member.");
		IsisSystem.Start ();
		myGroup = new Isis.Group (groupname);

		// Add handlers to the member.
		WriteLine (_localDebugFilename, "Add handlers.");
		addHandlers (myGroup);

		// Join the group.
		WriteLine (_localDebugFilename, "Joining the Group.");
		myGroup.Join ();

		// Initiate CloudMakeMonitor Thread on the local node.
		WriteLine (_localDebugFilename, "Add monitor thread.");
		monitorThread = new Thread (delegate() {
			LaunchMonitor (myGroup);
		}
		);
		monitorThread.Start ();

		return myGroup;
	}

	public static void WriteLine (string filename, string line)
	{
		Console.Out.WriteLine (line);
		using (StreamWriter file = File.AppendText(filename)) {
			file.WriteLine (line);
		}
	}

	public static bool IsUnix ()
	{
		int p = (int)Environment.OSVersion.Platform;
		return (p == 4) || (p == 6) || (p == 128);
	}

	public static void Main (string[] args)
	{
		bool debug;
		string hostname;
		string groupname;
		bool isLeader;
		string master;
		int nworkers;
		bool batching;
		bool parallel;
		int timeout;
		string masterIp = null;
		string localIp = null;
		bool isMaster = false;
		string debugDir = Directory.GetCurrentDirectory () + Path.DirectorySeparatorChar.ToString () + "Debug";
		string debugFilename = debugDir + Path.DirectorySeparatorChar.ToString () + "CloudMake.txt";

		// Take arguments by parsing the command line.
		try {
			var options = new Options ();
			Parser parser = new Parser ();

			if (!parser.ParseArguments (args, options)) {
				WriteLine (debugFilename, options.GetUsage ());
				System.Environment.Exit (0);
			}

			debug = options.debug;
			hostname = options.name;
			groupname = options.groupname;
			isLeader = options.leader;
			master = options.master;
			isMaster = false;
			nworkers = options.nworkers;
			batching = options.batching;
			parallel = options.parallel;
			timeout = options.timeout;

			if (debug) {
				Console.Out.WriteLine ("Directory Debug: " + Directory.Exists ("Debug").ToString ());

				if (!Directory.Exists ("Debug"))
					Directory.CreateDirectory ("Debug");

				if (isLeader) {
					if (File.Exists ("Debug" + Path.DirectorySeparatorChar + "CloudMakeLeader.txt"))
						File.Delete ("Debug" + Path.DirectorySeparatorChar + "CloudMakeLeader.txt");
					using (FileStream fs = File.Create("Debug" + Path.DirectorySeparatorChar + "CloudMakeLeader.txt")) {
					}
					;
				}

				if (File.Exists ("Debug" + Path.DirectorySeparatorChar + "CloudMake.txt"))
					File.Delete ("Debug" + Path.DirectorySeparatorChar + "CloudMake.txt");
				using (FileStream fs = File.Create("Debug" + Path.DirectorySeparatorChar + "CloudMake.txt")) {
				}
				;

				if (File.Exists ("Debug" + Path.DirectorySeparatorChar + "CloudMakeLocal.txt"))
					File.Delete ("Debug" + Path.DirectorySeparatorChar + "CloudMakeLocal.txt");
				using (FileStream fs = File.Create("Debug" + Path.DirectorySeparatorChar + "CloudMakeLocal.txt")) {
				}
				;

				if (File.Exists ("Debug" + Path.DirectorySeparatorChar + "CloudMakeMonitor.txt"))
					File.Delete ("Debug" + Path.DirectorySeparatorChar + "CloudMakeMonitor.txt");
				using (FileStream fs = File.Create("Debug" + Path.DirectorySeparatorChar + "CloudMakeMonitor.txt")) {
				}
				;
			}

			CloudMake.WriteLine (debugFilename, "Debug: " + debug.ToString ());
			CloudMake.WriteLine (debugFilename, "Batching: " + batching.ToString ());
			CloudMake.WriteLine (debugFilename, "Parallel: " + parallel.ToString ());


			if (master != null) {
				if (debug)
					WriteLine (debugFilename, "Resolving master IP Address...");
				masterIp = GetIpAddress (master, debugFilename);
				if (debug) {
					WriteLine (debugFilename, "Master IP: " + masterIp);
					WriteLine (debugFilename, "Resolving local IP Address...");
				}
				localIp = GetMyIpAddress (debugFilename);
				if (debug)
					WriteLine (debugFilename, "Local IP: " + localIp);
				if (masterIp.Equals (localIp))
					isMaster = true;
			}

			if (hostname == null)
				throw new Exception ("CloudMake: You need to specify a name for your instance.");

			if (nworkers < 0) {
				WriteLine (debugFilename,
				           "Parameter nworkers must be set to a non-negative number for the master node.");
				WriteLine (debugFilename, options.GetUsage ());
				System.Environment.Exit (0);
			}

			// Set Environmental Variables.
			Environment.SetEnvironmentVariable ("ISIS_HOSTS", masterIp);			// Unlock for EC2.
			Environment.SetEnvironmentVariable ("ISIS_UNICAST_ONLY", "true"); 		// Unlock for EC2.
			Environment.SetEnvironmentVariable ("ISIS_SUBNET", "10.0.0.0");			// Unlock for EC2.
			Environment.SetEnvironmentVariable ("ISIS_NETMASK", "255.0.0.0");		// Unlock for EC2.

			// Initiate CloudMake group.
			WriteLine (debugFilename, "Group: " + groupname + ", Node: " + hostname);
			WriteLine (debugFilename, "MasterMode: " + (master != null).ToString ());
			if (master != null)
				WriteLine (debugFilename, "Master: " + masterIp);
			WriteLine (debugFilename, "Is Leader: " + isLeader.ToString ());

			// Initiate Isis.
			CloudMake cloudMake = new CloudMake (debug, hostname, groupname, isLeader, batching, parallel, timeout);
			Isis.Group myGroup = null;

			if (isMaster)
				myGroup = cloudMake.beMaster (groupname, nworkers);
			else if (master != null)
				myGroup = cloudMake.beWorker (groupname, masterIp);
			else
				myGroup = cloudMake.beMember (groupname);

			// Wait for termination signals.
			WriteLine (debugFilename, "Normal Operation");
			if (isLeader) {
				WriteLine (debugFilename, "Leader launched.");
				cloudMake.LaunchCloudMakeLeader (myGroup);
			} else {
				if (CloudMake.IsUnix ()) {
					UnixSignal[] signals = new UnixSignal[] {
						new UnixSignal (Signum.SIGINT),
						new UnixSignal (Signum.SIGTERM),
					};

					WriteLine (debugFilename, "Got a " + signals [UnixSignal.WaitAny (signals, -1)].Signum + " signal!");
					IsisSystem.Shutdown ();
				} else {
					Console.CancelKeyPress += delegate {
						WriteLine (debugFilename, "Got a CTRL-c signal!");
						IsisSystem.Shutdown ();
					};
					Thread.Sleep (System.Threading.Timeout.Infinite);
				}
			}
		} catch (Exception e) {
			WriteLine (debugFilename, "Error " + e.ToString ());
		}
	}
}